本地事件总线和消息队列都用了观察者思想,它们怎么选?
简化版
本地事件总线适合同进程内解耦,调用轻、延迟低、实现简单,但不适合跨服务可靠投递;消息队列适合跨进程异步通信、削峰、重试和可靠投递,但引入运维、序列化、幂等和最终一致性成本。选择时看边界:同应用内非关键扩展用本地事件;跨服务、需要可靠、需要削峰或消费者独立扩缩容时用 MQ。
详细版
本地事件总线和 MQ 都体现了观察者思想:发布方发布事件,订阅方响应事件。但它们解决的问题不一样。
本地事件总线:
same process:
publisher -> event bus -> listener A/B/C
消息队列:
service A -> broker -> service B/C/D
本地事件总线更轻,适合应用内模块解耦;MQ 更重,但能跨进程、跨服务,支持可靠投递和流量缓冲。
记忆钩子:本地事件总线解耦代码模块,消息队列解耦服务和流量。
完整版教学
面试提示:本题不要只比较“本地快、MQ 稳”,要围绕事务边界、可靠性、扩展范围和故障隔离说明选型。
一、两者都像观察者,但边界不同
观察者模式的核心是一对多通知。本地 EventBus 把这种通知放在进程内;MQ 把这种通知扩展到进程外、服务间。
观察者模式思想
-> 本地事件总线: 内存级通知
-> 消息队列: 分布式通知
所以不要只因为“都是发布事件”就把它们当成同一个东西。
二、本地事件总线的优势
本地事件总线适合:
- 同一个应用内部模块解耦;
- 监听器执行很快;
- 不要求进程崩溃后继续投递;
- 不需要跨服务消费;
- 事件量较小。
例如订单模块发布 OrderPaidEvent,同应用内的日志、指标、缓存刷新监听器响应。
优势是简单、延迟低、没有序列化和网络成本。
三、本地事件总线的局限
局限也明显:
| 问题 | 说明 |
|---|---|
| 进程崩溃 | 内存事件可能丢失 |
| 跨服务 | 无法直接通知其他进程 |
| 削峰 | 无法像 MQ 一样堆积流量 |
| 重试 | 通常要自己实现 |
| 可观测 | 需要自己补监听器状态 |
如果订单支付后必须通知仓储系统发货,只靠本地 EventBus 通常不够。
四、消息队列适合跨服务可靠通知
MQ 适合:
- 跨服务通信;
- 异步削峰;
- 消费失败重试;
- 消费者独立扩容;
- 事件可靠投递;
- 业务最终一致性。
典型链路:
订单服务 -> OrderPaid 消息 -> MQ
-> 仓储服务消费
-> 积分服务消费
-> 营销服务消费
发布方不需要知道有几个下游服务,下游也可以独立扩容。
五、MQ 的代价也要说清
MQ 不是“高级版观察者”这么简单,它会带来:
- 消息重复;
- 消息延迟;
- 顺序消费限制;
- 消费幂等;
- 死信队列;
- 消息堆积;
- 运维和监控;
- 事件 schema 演进。
所以同进程内一个简单缓存刷新,不要动不动上 MQ。
六、可以组合使用
实际项目经常组合:
应用内:
DomainService -> local event -> write outbox
应用外:
outbox relay -> MQ -> downstream services
也可以主流程先写本地事件表,再由异步任务发 MQ。这样兼顾事务一致性和跨服务通知。
七、选择标准要落到业务问题
| 判断问题 | 倾向本地事件总线 | 倾向 MQ |
|---|---|---|
| 是否跨服务 | 否 | 是 |
| 是否要求可靠投递 | 低 | 高 |
| 是否需要削峰 | 否 | 是 |
| 消费者是否独立扩容 | 否 | 是 |
| 是否能接受最终一致性 | 不一定 | 通常需要 |
| 运维复杂度 | 低 | 高 |
面试中最好用这些维度回答,而不是说“MQ 更强”。
八、常见误区与追问
- 误区:本地 EventBus 可以替代 MQ。 它无法解决跨进程可靠投递、削峰和消费者独立扩容。
- 误区:MQ 一定比本地事件好。 简单应用内解耦用 MQ 会增加不必要复杂度。
- 误区:用了 MQ 就不用观察者模式。 MQ 仍然体现发布订阅思想,只是工程边界更大。
- 误区:本地事件一定同步,MQ 一定异步。 本地也可异步,MQ 也可能被同步等待,但典型语义不同。
- 追问:订单支付后发短信用哪个? 同应用内非关键短信可本地异步;跨短信服务或要求可靠重试时用 MQ。
- 追问:本地事件丢失怎么办? 需要可靠时用事件表、Outbox 或 MQ,不要只靠内存事件。
- 追问:为什么 MQ 要幂等? 至少一次投递、重试、rebalance 都可能让消费者重复收到消息。
九、加强记忆
- 本地事件总线用于进程内模块解耦。
- MQ 用于跨服务、可靠投递、削峰和独立扩容。
- 本地事件简单低延迟,但进程崩溃可能丢。
- MQ 能力强,但带来重复、延迟、幂等和运维成本。
- 可靠跨服务事件常结合 Outbox 和 MQ。
- 选择看边界和可靠性,不看名字像不像。