我正在使用微服务方法构建应用程序。对于服务之间的通信,我将Symfony Messenger与RMQ传输一起使用。基本上,一切正常,但是我所有的服务都必须在同一个命名空间中。一旦我尝试将它们分成自己的命名空间,例如App\Mail
,App\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
我希望将应用程序保留在不同的名称空间中,并且仍然能够处理来自其他服务的事件
答案 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: ~
请记住,这更像是概念证明,它可能可以增强,但是可以正常工作。