连载中 19/20

陷阱清单:丢失、重复、乱序的一线复盘

2026-08-09 · 2709 阅读 · 0 评论 · 0 赞

事故是最好的教材

这一篇没有新知识,只有七起「别人已经替你踩过」的坑。每起事故三段式:现象、根因、修复。读的时候请对号入座——像不像你上周写的代码?

事故一:异步发送吞异常,三千张发票凭空消失

现象:月底对账,3000 笔订单没开发票,日志里一片干净。根因:异步发送的 onException 回调是空的,网络抖动时消息无声丢失(第 5 篇死法二)。修复:回调异常落库重试 + 告警;核心链路上本地消息表。

事故二:自动提交 offset,批处理「被完成」

现象:Kafka 消费者重启后,一段消息彻底没了。根因:enable.auto.commit=true,poll 拉到消息就按周期自动提交,业务处理到一半崩溃,位点已经飞了(第 6 篇的坑)。修复:关自动提交,处理完成后手动 commitSync;Rebalance 监听器里补一次提交。

事故三:Redis 幂等失效,优惠券发成双份

现象:用户投诉券发了两张。根因:用 setnx 做幂等开关,但 Redis 与数据库不是原子:setnx 成功后业务落库前 Redis 宕机重启,key 丢了,重投的消息畅通无阻(第 7 篇警告过)。修复:去重表与业务同事务,Redis 降级为纯加速层。

事故四:Rebalance 风暴,重复扣款 400 笔

现象:发布期间支付通知消息大量重复消费。根因:单批消息里有一条调外部接口要 8 秒,一批 500 条处理超过 max.poll.interval.ms,Kafka 误判消费者死亡触发全组 Rebalance,位点回退重放(第 14 篇)。修复:max.poll.records 降到 50、外部调用设 2 秒超时、启用静态成员;扣款逻辑补上幂等键。

事故五:毒消息卡死顺序队列两小时

现象:某 Topic 八个队列里有一个消费 TPS 为零,其余七个正常。根因:顺序消费遇到坏消息原地挂起重试,没有上限也没有告警(第 8、11 篇的雷)。修复:重试上限 + 死信转出 + 队列级消费延迟监控。

事故六:一张 base64 图片打瘫整个集群

现象:Broker 发送耗时全面上涨,磁盘 IO 打满。根因:开发把 4MB 的商品图 base64 塞进消息,CommitLog 大文件混写被大消息搅乱,顺序写退化(第 13、15 篇)。修复:图片走对象存储,消息只传 URL;客户端限制单条消息上限。

事故七:手滑重置位点,旧消息灌进新系统

现象:凌晨告警,百万条历史消息涌入消费端,下游数据库连接打满。根因:值班同学排障时误把消费位点重置到最早(earliest)。修复:位点重置操作收权 + 双人复核;消费端限流保护;事后靠幂等与对账把数据对平。

根因归类表

根因类别对应事故系统防线
异常/确认被吞一、二手动提交、回调必处理、本地消息表
幂等不彻底三、四同事务去重表、状态机
异常路径无上限五、七重试上限、死信告警、操作收权
消息体失控体积限制、引用代替内容

MQ 代码评审清单

  • 发送:没有 oneway?异步回调处理了异常?核心链路有本地消息表?
  • 消费:自动提交关了?业务成功后才 ACK?消费逻辑幂等吗?幂等是同事务的吗?
  • 顺序:真的需要顺序吗?毒消息有上限和告警吗?
  • 体积:消息里塞了不该塞的大对象吗?
  • 兜底:对账任务有吗?死信有人管吗?

小结

七起事故,四类根因,每一起的修复都指向前面十几篇讲过的基本功。事故不源于知识盲区,源于「知道但没做」——清单的价值就是把它变成必答题

下一站,收官:一张消息队列作战地图,20 篇内容收进一张表,出事时直接翻。

503

10 年全栈工程师 · 503咖啡馆主理人

#消息队列#事故复盘#陷阱清单#幂等失效#消息丢失

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 4 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞