2

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

例如,Auth应用程序调度事件UserRegistered,因此消息的类型为App\Auth\Event\UserRegistered. 我想在我的邮件应用程序中处理该事件,但信使无法使用它,因为我的事件和处理程序位于App\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.

在这个确切的示例中,我正在从命名空间下的应用程序发送事件 UserRequestedPasswordReset App,并且我正在尝试将它与App\Mail命名空间下的应用程序一起使用。

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

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

我的一项服务中的示例信使配置。除了 event 我使用 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

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

4

1 回答 1

4

因此,在深入研究主题后,我能够找到解决方案。

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

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;
    }
}

在信使配置中:

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

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

于 2019-08-02T14:44:08.283 回答