Skip to content

线上消息队列故障,兜底改造方案 ​

  • 标签:后端、线上问题排查

🏛 业务架构师 ​

线上 MQ 故障的兜底改造,核心思想一句话:MQ 是加速器不是地基——核心交易链路必须设计成 MQ 挂了也能降级运转。我分三层讲:事前架构、事中止损、事后恢复。

事前:架构上预留降级路径

  • 写路径降级:订单创建后的后续动作(发券、通知、积分)走 MQ,MQ 不可用时自动切到本地消息表——消息和业务数据同库落盘,定时任务补偿投递。相当于从"实时投递"降级为"延迟投递",业务不中断
  • 读路径降级:依赖 MQ 更新的缓存/视图,降级为直接查库 + 限流,牺牲一点 RT 保正确性
  • 隔离与限流:MQ 恢复瞬间会有积压洪峰,消费端和被同步系统要有独立的容量与限流,防二次事故

事中:止损动作

  • 快速确认故障范围(单 topic 还是全集群)
  • 核心链路切降级开关(预案必须是演练过的,不是现场写代码)
  • 评估积压量与业务影响,决定是否冻结非核心消息生产(日志、通知类)

事后:恢复与补偿

  • 按消息时间戳/offset 补投,消费端幂等保证重复投递无副作用
  • 对账:降级期间本地消息表的记录 vs 实际消费结果,逐条核对闭环
  • 复盘落到开关自动化(MQ 健康探测自动切换)而不是靠人盯

业务视角的投入产出:本地消息表方案的开发成本约一两个迭代,换来的是 MQ 故障时交易可用性不打折。我做架构评审时必问一句话:"这个组件挂了,业务还活着吗?"——答不上来的设计,就是不达标的设计。

🏗 系统架构师 ​

线上 MQ 故障时,我的原则是:可用性优先,链路降级但不中断。分事前容灾设计和事中应急处置讲。

事前:架构上就不允许 MQ 是单点

  • MQ 集群多节点 + 多机房部署,生产端确认机制(ack)防丢
  • 核心链路评估:哪些消息丢了影响资损(支付成功),哪些只是体验(积分),按等级定降级策略

事中:分层降级方案

  1. 生产端本地兜底:MQ 不可用时,消息落本地磁盘(生产端"本地消息表 + 刷盘文件"),后台任务重试投递,恢复后自动补发。要点是磁盘队列也要有容量上限与告警
  2. 同步兜底:对实时性要求高的少量关键消息,降级为同步 RPC 调用直接触发下游(比如支付成功直接同步通知订单服务),但要限流保护下游
  3. 消费端反压:MQ 只是堆积而非全挂,消费端扩容消费者实例 + 提高并行度;如果是下游处理慢导致堆积,先扩下游再扩消费者,否则堆的是下游
  4. 读写路径分离:非核心的异步动作(发短信、埋点)直接熔断丢弃或降级日志,把资源让给核心消息

事后:补偿任务扫描本地消息表与对账,确保"故障期间不丢不重";复盘补齐 Chaos 演练,把"MQ 挂 N 分钟"纳入常态化容灾演习。

一句话:生产端本地消息表保数据不丢,同步降级保核心链路,分级熔断保资源。

💻 全栈开发专家 ​

MQ 故障的兜底目标:消息不丢、可恢复、核心链路不倒。我做三层改造。

1. 生产端:本地消息表(核心方案)

sql
CREATE TABLE local_message (
  id BIGINT PRIMARY KEY,
  msg_id VARCHAR(64) UNIQUE,      -- 消费幂等键
  topic VARCHAR(64), payload JSON,
  status TINYINT,                 -- 0待发送 1已发送 2死亡
  next_retry_time DATETIME,
  retry_count INT
);

业务与消息记录同库同事务落库,后台任务(XXL-JOB 每 10s)扫描待发送消息投递 MQ,成功后标记。MQ 整体不可用时消息先稳稳躺在表里,恢复后自动追平——这就兜住了"发不出去"。

2. 消费端:失败不无限重试

  • 异常消息重试 N 次后落死信表人工/自动处理,别 block 后续消息。
  • 消费侧加开关:MQ 抖动时降级为"写库 + 定时补偿"。

3. 依赖治理

  • 客户端配置合理超时与退避重试,避免 MQ 恢复瞬间被重连风暴打死。
  • 发送侧加熔断:MQ 连续失败直接走本地消息表,不再同步等待,保证业务 RT 不被 MQ 拖垮。

事后复盘要点 这次故障要推动:MQ 集群多机房容灾、Topic 副本数审查、发送端成功率大盘 + 堆积告警。兜底方案的价值就是把"级联故障"降级为"延迟到达"。