Skip to content

约 5 分钟阅读

消息中间件全景:选型、能力与落地 ​

订单支付成功后,用户需要及时看到支付结果,邮件和搜索索引可以稍后完成。消息中间件(MQ)保存待处理的事件,让这些下游各自消费和重试;支付主链路与邮件服务的快慢由此分开。

选型要先回答:任务是否需要回放、失败能否重试、同一订单是否必须保序,以及多久必须处理完。下图先说明可靠事件链路,再对比 Kafka、RabbitMQ、RocketMQ 与 Pulsar 的模型和适用边界。

MQ 在系统中的位置 ​

MQ 在系统中的位置

核心变化是:主链路只完成必要的业务提交,耗时或可异步的动作交给 MQ 后面的消费者处理。

例如支付请求只需要确认支付流水与订单状态已经提交,发券、邮件与搜索索引可异步完成。不能先提交数据库、再随手发一条消息就宣称可靠:进程可能恰好在两步之间退出。需要同一数据库事务写业务记录与 Outbox,再由中继重试发布;中继可能重复发送,消费端仍要幂等。

视频学习入口 ​

系列链接搭配阅读
Kafka 核心原理科普从第 01 集开始看完后阅读 Kafka,重点补 Topic、Partition、Consumer Group、事务和容量规划
RocketMQ 核心原理科普RocketMQ 01看完后阅读 RocketMQ,重点补事务消息、延迟消息、顺序消息和存储结构
RabbitMQ暂无配套视频先阅读 RabbitMQ,重点理解 Exchange、Queue、Binding、Confirm、Ack、TTL 与 DLX

MQ 解决的典型问题 ​

问题不用 MQ 的表现使用 MQ 后
应用解耦A 服务直接调用 B/C/D,任一下游失败影响主链路A 只发布事件,下游各自订阅
削峰填谷高峰流量直接打爆数据库或第三方接口消息先进入队列,消费者按能力处理
异步任务用户请求等待慢操作完成请求快速返回,后台异步执行
广播通知逐个调用订阅方多消费者订阅同一事件
事件回放历史事件丢失,无法重建状态保留消息后可重新消费
最终一致性分布式事务复杂通过事件、重试、幂等、补偿实现

主流组件能力地图 ​

组件核心模型最强能力典型场景选型关键词
RabbitMQExchange + Queue灵活路由、任务队列、协议生态业务异步、任务分发、微服务解耦好上手、路由强
KafkaTopic + Partition Log高吞吐、可回放、流处理生态日志、埋点、CDC、实时数据管道事件流、数据平台
RocketMQTopic + Queue事务消息、顺序消息、延时消息订单、支付、库存、交易链路业务语义强
PulsarTopic + Subscription + BookKeeper多租户、存算分离、多订阅模型云原生统一消息平台、跨地域平台化、多租户

选型判断 ​

消息中间件选型流程

这些能力可以重叠,图中给出评估入口,不是互斥判定。已经有 Kafka 的团队,为订单发券再引入一套 MQ 未必划算;先确认延迟精度、恢复目标、协议和运维能力,再用实际消息大小、峰值和故障恢复场景做验证。

消息投递的基本语义 ​

语义含义风险落地建议
至多一次最多投递一次可能丢消息只适合日志、指标等可丢场景
至少一次至少成功投递一次可能重复最常见,消费者必须幂等
精确一次指定处理边界内,结果提交一次越界副作用不自动覆盖明确事务覆盖范围,再设计外部系统幂等

现实工程里不要幻想“MQ 帮我保证业务绝对只执行一次”。更可靠的做法是:MQ 至少一次 + 消费者幂等 + 业务状态机约束。

“至少一次”也有前提:生产成功被确认、消息未过期或被保留策略删除、消费者在可恢复期限内持续重试。MQ 不会在磁盘耗尽、保留期已过或死信无人处理时承诺业务最终完成。

一个标准消息处理链路 ​

标准消息处理链路

数据库提交后、消费确认前进程退出,是重复投递的典型窗口。发券记录和处理记录应在同一个本地事务提交,以业务唯一键限制重复。外部发券接口则需要支持幂等请求键或查询对账,仅在本地先写“已处理”会丢任务,先调用后写记录又可能重复调用。Kafka 的重试主题和死信需要应用或框架设计,并非 Broker 在“不提交 offset”后自动帮业务跳过失败消息。

落地前必须设计的 8 件事 ​

  1. 消息契约:字段、版本、兼容策略、事件命名。
  2. Topic/Queue 规划:按业务域拆分,不要所有消息共用一个主题。
  3. 消息键设计:用于分区、顺序、幂等和排查。
  4. 投递语义:至少一次、至多一次,还是需要事务协同。
  5. 幂等策略:唯一键、防重表、状态机、乐观锁。
  6. 失败策略:重试次数、退避间隔、死信、人工补偿。
  7. 消息生命周期:TTL、保留时间、归档、清理。
  8. 监控告警:堆积、延迟、失败率、重试量、死信量。

统一案例:订单超时关闭 + 支付成功发券 ​

这套案例会在每篇组件文档里展开:

订单超时关闭与支付成功发券

核心业务规则:

  • 下单 30 分钟未支付,自动关闭订单。
  • 支付成功后异步发券。
  • 超时关闭和支付成功存在竞态,订单状态机必须兜底。
  • 发券必须幂等,不能重复发。

不同组件实现方式:

组件推荐实现
RabbitMQ固定延时队列 TTL + DLX 触发检查,核对死信转发可靠性
Kafka事件流 + 调度服务,适合可回放和审计
RocketMQ延时消息 + 事务消息,最贴近交易业务语义
PulsarDelayed Delivery + Shared/Key_Shared,超时与支付竞态由数据库仲裁

收到超时消息后执行“查询未支付,再无条件关闭”仍有竞争窗口:查询与更新之间,支付回调可能先提交。订单服务应使用类似下面的条件更新,并检查影响行数;支付流程也只能从允许的状态迁移。

sql
UPDATE orders SET status = 'CLOSED'
WHERE id = :order_id
  AND status = 'PENDING'
  AND expires_at <= CURRENT_TIMESTAMP;

这里是带命名参数的 SQL 逻辑片段,参数由数据库访问层绑定。影响 0 行表示没有取得关闭资格,需要检查是已支付、已关闭还是未到期;已经到账但订单已关闭的支付,要进入退款或人工对账策略。验收时至少覆盖消息重复、消费后确认前退出、超时和支付同时到达、下游停机后恢复四种情况。

别急,先让缓存热一下。