Symfony Messenger 传输:在顶层构建事务性出... 笔记

Symfony Messenger 传输:在顶层构建事务性出站盒

双写问题发生在应用程序向数据库记录写入数据,随后向消息代理发布事件,但其中一项操作失败时。这可能导致不一致的情况:订单存在于数据库中,但下游系统未收到通知,反之亦然,从而引发诸如幽灵订单和退款等问题。由 Symfony Messenger 支持的事务性出箱模式(Transactional Outbox Pattern)提供了一种稳健的解决方案。其核心思想是在与主数据写入相同的数据库事务中,将事件插入到数据库表(即出箱表)中。这确保了原子性:订单和事件记录要么一起提交,要么都不提交。Symfony Messenger 的 Doctrine 传输(transport)可被配置为充当此出箱表。通过将事件路由到 Doctrine 传输,事件消息的 INSERT 操作会被包含在现有的数据库事务中。事务提交后,一个独立进程(通常是 messenger:consume)充当中继。该中继从出箱表中读取消息并将其分发到其目标目的地,例如 RabbitMQ。这将事件分发与初始请求处理解耦,使系统能够抵御代理故障或临时重启。messenger:consume 命令使用 SELECT FOR UPDATE SKIP LOCKED,允许多个工作进程并发处理出箱表中的消息,同时避免重复处理。至关重要的是,出箱模式保证至少一次投递(at-least-once delivery),这意味着处理器必须是幂等的,以安全地处理潜在的消息重放。这可以通过存储稳定的事件 ID 并在执行任何副作用之前检查已处理事件表来实现。通过将事件路由到 Doctrine 传输并使用 messenger:consume 作为中继,应用程序可以在无需自定义基础设施的情况下实现可靠的事件投递。这种方法将基础设施关注点置于边缘,使领域层能够专注于业务逻辑,并便于未来对消息传输进行更改。
CdXz5zHNQW_UmnYYwwYW3.webp