为什么消费端比生产端更难

生产端只管把消息发出去,消费端却要面对"消息可能重复、可能失败、可能乱序、可能堆积"这一整串麻烦。生产端发完即走,消费端要在不可靠的投递下把业务正确落地,这才是消息队列真正的难点所在。

本文核心知识点
  • 幂等重复消费不产生重复结果。
  • 重试失败消息按退避策略重投。
  • 死信队列多次失败的消息集中处理。
  • 积压消费跟不上生产时的排查思路。
消息队列消费端配图:蓝色渐变背景左侧生产方块投入消息卡片到横向管道,中间消费者方块读取,右下死信回收方块,扁平科技插画
生产、消费、死信回收三段流,正是消费端完整生命周期的意象。

至少一次投递带来的重复

为了不丢消息,消息队列普遍采用"至少一次(at-least-once)"投递:消费端处理完还没来得及确认,进程就挂了,队列会把这条消息再投给另一个消费者。于是同一条消息被消费两次几乎是必然事件,消费端必须假设"消息可能重复"。

消费幂等怎么保证

最直接的办法是给每条消息一个唯一业务键,消费时先查"这个键处理过没有",处理过就直接确认跳过。常见做法:用消息 ID 落一张去重表、或利用业务表的唯一约束(同一订单号只能扣一次)。

// 伪代码:先占坑再处理
if (dedup.has(msg.bizId)) return ack();   // 已处理,跳过
dedup.mark(msg.bizId);
process(msg);                            // 处理业务
ack();

重试与退避

业务依赖的下游抖动时,立刻重试往往还是失败。应该用指数退避:第一次失败等 1 秒、再失败等 5 秒、再等 30 秒,给下游恢复时间。重试要有上限,超过上限就别一直循环,否则会把坏消息反复重投、拖垮消费能力。