消息的重发补偿解决思路
- 可靠消息服务定时查询状态为已发送并超时的消息
- 可靠消息将消息重新投递到 MQ 组件中
- 下游应用监听消息,在满足幂等性的条件下,重新执行业务。
- 下游应用通知可靠消息服务该消息已经成功消费。
- 通过消息状态确认和消息重发两个功能,可以确保上游应用、可靠消息服务和下游应用数据的最终一致性。
五条描述的是”本地消息表/可靠消息服务”模式的重发闭环。要把它落地成可靠系统,还需补全:消息丢失的三个环节与各自对策(重发只解决其一)、本地消息表与事务消息两种实现形态、以及重发策略的参数设计。本文补全成完整的可靠性方案。
消息丢失的三环节与对策全景
| 环节 | 丢失原因 | 对策 |
|---|---|---|
| 生产→Broker | 发送超时/Broker 宕机 | 同步发送+重试 / 事务消息 / 本地消息表补偿(本文重点) |
| Broker 存储 | 未刷盘宕机 / 副本未同步 | 同步刷盘/同步复制配置(权衡吞吐) |
| Broker→消费 | 自动 ack 后消费失败 / 重平衡丢消息 | 手动 ack(处理成功才确认)+ 消费失败重试 |
重发补偿解决的是第一环节(生产侧不确定)与消费失败的再投递;三环节要各自设防才叫端到端可靠。
实现形态一:本地消息表(原文方案的完整化)
- 上游业务事务内:写业务表 + 写消息表(status=待发送)——同事务保证”业务成功必有消息记录”
- 独立发送线程/定时任务:扫消息表投递 MQ,成功置 status=已发送
- 定时补偿:扫”已发送但超时未确认”的记录重投(原文第 1、2 条)
- 下游消费成功:回调/消息确认置 status=已完成(原文第 4 条)
- 下游消费幂等:重投的消息靠幂等键去重(原文第 3 条)
要点:消息表与业务表同库同事务(跨库则失去原子性);补偿扫描的”超时”要大于正常确认时长防误重投;消息表定期归档清理。
实现形态二:事务消息(RocketMQ 原生)
- 发 half 消息(对消费者不可见)
- 执行本地事务
- 提交/回滚 half 消息(commit 则投递、rollback 则丢弃)
- 回查:Broker 长时间未收到二阶段结果时反查本地事务状态——替代本地消息表的定时扫描
优势:无消息表、无扫描任务;代价:绑定支持事务消息的 MQ(RocketMQ),回查逻辑仍需实现(查业务状态)。
重发策略参数设计
- 重试次数与退避:3-5 次、指数退避(1s/5s/30s/2m/10m),避免故障期反复冲击
- 重试载体:MQ 原生重试队列(RocketMQ %RETRY% topic / RabbitMQ dead-letter+delay)或补偿任务重投
- 终点是死信:重试耗尽进死信队列 + 告警 + 人工/脚本补偿——重发不是无限重试,而是”重试+兜底”的闭环
- 幂等键贯穿:消息 ID/业务键在重投中不变,下游去重表据此判重(重发安全的前提)
最终一致性的确认语义
原文第 5 条的”最终一致性”要精确化:
- 上游业务成功 ⇒ 消息最终必被成功消费一次(幂等意义下)——靠”同事务落表 + 补偿重投 + 幂等消费”三角保证
- 不保证时限(最终=有界重试+死信人工兜底内的”最终”)
- 对账任务兜底:业务表与下游状态周期比对,修复极端遗漏(补偿的补偿)
与 at-least-once 语义的关系
MQ 的 at-least-once(至少一次)+ 消费幂等 = effectively-once(效果上恰好一次)。重发补偿是生产侧的 at-least-once 保证,幂等是消费侧的去重保证,两者相乘才是端到端恰好一次——只谈重发不谈幂等,重发就是重复事故制造机。
重发补偿的思路一句话:**把”不确定的发送”变成”确定的记录 + 不确定的投递 + 确定的补偿”**;本地消息表与事务消息是它的两种载体,退避重试与死信兜底是它的策略两翼,幂等消费是它安全的前提。五者齐备,最终一致性才从口号变成可验收的系统性质。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus










