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

时间:2019-07-30 08:05:44

标签: symfony symfony-messenger

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

例如,Auth应用程序调度事件UserRegistered,因此消息的类型为App\Auth\Event\UserRegistered。我想在我的Mail应用程序中处理该事件,但Messenger无法使用它,因为我的事件和处理程序位于App\Mail命名空间下,因此它无法在“ Mail”应用程序中找到App\Auth\Event\UserRegistered类。

我得到的示例错误:

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命名空间下的应用程序中使用它。

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

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

我的一项服务中的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

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

1 个答案:

答案 0 :(得分:0)

因此,深入研究主题之后,我便找到了解决方案。

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

class EventsSerializer extends Serializer
{
    /**
     * {@inheritdoc}
     */
    public function encode(Envelope $envelope): array
    {
        $data = parent::encode($envelope);

        $data['headers']['type'] = $this->parseType($data['headers']['type']);

        return $data;
    }

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

        $encodedEnvelope['headers']['type'] = $translatedType;

        return parent::decode($encodedEnvelope);
    }

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

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

        if (array_key_exists($type, $map)) {
            return $map[$type];
        }

        return $type;
    }
}

然后进入服务

services:
    _defaults:
        autowire: false
        autoconfigure: false

    events_serializer:
        class: App\Infrastructure\Messenger\RabbitMQ\Serializer\Events
        autowire: true

并使用Messenger配置:

framework:
    messenger:
        default_bus: messenger.bus.commands
        serializer:
            default_serializer: events_serializer
            symfony_serializer:
                format: json
                context: { }
        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)%"
                serializer: events_serializer
                options:
                    exchange:
                        name: ecommerce_events
                        type: fanout
                    queues:
                        ecommerce_mail: ~

请记住,这更像是概念证明,它可能可以增强,但是可以正常工作。