使用 RMQ 在 Symfony Messenger 中处理来自不同命名空间的消息

Handling messages from different namespaces in Symfony Messenger with RMQ

我正在使用微服务方法构建应用程序。对于服务之间的通信,我使用带有 RMQ 传输的 Symfony Messenger。基本上一切正常,但我所有的服务都必须在同一个命名空间中。一旦我试图将它们分成它们自己的命名空间,如 App\MailApp\Auth 等,Messenger 就会抱怨缺少 Event classes,因为整个命名空间是在 header 中提供的消息发送到 RMQ。有什么方法可以映射来自两个不同命名空间的事件吗?

例如 Auth 应用分派事件 UserRegistered 因此消息的类型为 App\Auth\Event\UserRegistered。我想在我的邮件应用程序中处理该事件,但 Messenger 无法使用它,因为我的事件和处理程序位于 App\Mail 命名空间下,因此它无法在 [=] 中找到 App\Auth\Event\UserRegistered class 39=] 应用程序。

我遇到的示例错误:

In Serializer.php line 85:

  Could not decode message: Could not denormalize object of type App\Event\UserRequestedPasswordReset, no supporting normalizer found.

在这个确切的例子中,我从 App 命名空间下的应用程序发送事件 UserRequestedPasswordReset,我试图在 App\Mail 命名空间下的应用程序中使用它。

我无法在文档中或通过 Internet 找到任何有用的信息。我试图在容器中将 App\Event\UserRequestedPasswordReset 别名为 App\Mail\Event\UserRequestedPasswordReset 但没有成功。我猜这对 Denormalizers 来说是可行的,但在互联网上也找不到任何有用的东西。

通信本身正常,消息发送到 RMQ 并在其他服务中接收。我的 RMQ 设置是: 我有多个 queues,每个服务一个。我与那些 queues 绑定的扇出交换。每当我制作活动时,我都会发布它以进行交换以将其填充给所有 queues,以便感兴趣的服务可以处理它们。

我的一项服务中的 Messenger 配置示例。除了事件之外,我还使用 Messenger 来处理 CQRS 命令和查询,所以我使用了三种不同的总线。

messenger:
        default_bus: messenger.bus.commands
        buses:
            messenger.bus.commands:
                middleware:
#                    - validation
#                    - doctrine_transaction
            messenger.bus.queries:
                middleware:
#                    - validation
            messenger.bus.events:
                default_middleware: allow_no_handlers
                middleware:
#                    - validation
        transports:
            events:
                dsn: "%env(MESSENGER_AMQP_DSN)%"
                options:
                    exchange:
                        name: ecommerce_events
                        type: fanout
                    queue:
                        name: ecommerce_auth

        routing:
            'App\Event\UserCreated': events
            'App\Event\UserModified': events
            'App\Event\UserChangedPassword': events
            'App\Event\UserRequestedPasswordReset': events

我想将我的应用程序保留在不同的命名空间中,并且仍然能够处理来自其他服务的事件

所以在深入研究主题后,我找到了解决方案。

我只需要创建自定义序列化程序,然后在编码过程中剥离名称空间,然后在解码过程中我提供类型映射到实际事件 class。这是我的代码

class EventsSerializer extends Serializer
{
    public function encode(Envelope $envelope): array
    {
        $data = parent::encode($envelope);
        $data['headers']['type'] = $this->parseType($data['headers']['type']);

        return $data;
    }

    private function parseType(string $type)
    {
        return end(explode('\', $type));
    }

    public function decode(array $encodedEnvelope): Envelope
    {
        $translatedType = $this->translateType($encodedEnvelope['headers']['type']);
        $encodedEnvelope['headers']['type'] = $translatedType;

        return parent::decode($encodedEnvelope);
    }

    private function translateType($type)
    {
        $map = [
            'UserCreated' => UserCreated::class,
            'UserRequestedPasswordReset' => UserRequestedPasswordReset::class
        ];

        return $map[$type] ?? $type;
    }
}

在 Messenger 配置中:

framework:
  messenger:
    serializer:
      default_serializer: AppBundle\Serializer\EventsSerializer

请记住,这更像是概念验证,它可能会得到增强,但它确实有效。