贴吧式
消息重试的退避策略与死信队列:你们到底按什么规则决定"重试几次、退多久、最后落哪儿"
帖子正文与楼层回复。
楼主 江墨白-195
消息中间件重试死信队列退避策略分布式一致性
背景:聊"至少一次 + 幂等"、聊消费端幂等/顺序/重试三角的帖子不少了,但真正落地时最容易被糊弄过去、也最容易在凌晨把人叫醒的,其实是重试本身——重试几次、每次退多久、退避用固定还是指数、要不要加抖动、超过上限之后消息落到哪儿、谁来负责把它捞回来。我带团队踩过的坑几乎都是这一层:不是幂等没做,而是重试策略配得太"顺眼",平时没事,一碰下游抖动就放大成重试风暴。
先抛几种我见过的、都很"合理"但后果差很多的做法,大家对照一下:
- 固定间隔重试 N 次(比如 3 次、每次 500ms)。优点是简单、可预期;缺点是当下游(DB、RPC、第三方)慢恢复时,所有失败消息会在同一时刻齐刷刷再打一遍,把 1 次瞬时抖动放大成 N 波冲击。
- 指数退避(1s → 2s → 4s → …)不加分散。比固定好,但"齐刷刷"的问题还在,只是间隔拉开了。
- 指数退避 + 随机抖动(jitter / full-jitter)。这是我现在最倾向的默认值:第 n 次重试的等待时间在 [0, base * 2^n] 里取随机值。能显著削平重试的同步峰值。
再抛几个我真正想听大家经验的分叉点:
- 重试上限怎么定?是"次数"(比如最多 5 次)还是"时间窗口"(比如 10 分钟内存活),还是干脆不设上限、靠外部调度重投?不同选法对"下游恢复慢于上限"这种场景的行为完全不同。
- 死信队列(DLQ):超过上限后是落 DLQ 等人工,还是直接告警 + 丢弃?落 DLQ 之后,谁来消费、多久消费一次、能不能安全重放?我见过 DLQ 变成"消息坟场",堆了几十万条没人敢动,因为没人确认这些消息到底幂等不幂等。
- 退避参数怎么算:base 和最大间隔你们是按下游 SLA、GC 周期、还是拍脑袋定的?有没有把"重试预算"当成容量的一部分纳入容量规划(这点和热榜 23 号"超时与重试是容量规划一部分"那篇是同一个思路,但想听具体数字)。
- 重试时是否要改变消息体或加元数据(比如带上已重试次数、原始 traceId、失败原因),方便事后重放和排查?
我的倾向(供拍砖):默认 full-jitter 指数退避,重试上限按"时间窗口"而非"次数"来卡(给下游一个明确的恢复预算),超过窗口落 DLQ 并立刻告警,DLQ 里每条必须带完整的失败上下文(重试次数、每次失败原因、traceId),并且上线时就写死"重放 SOP"——谁有权限、怎么重放、重放前怎么验证幂等。理由只有一个:重试策略不是"加个配置",它是你把瞬时故障放大成系统性故障的那根杠杆,杠杆长度必须是你算过、而不是抄来的。
想听大家:你们在有副作用的业务链路(订单/支付/库存)和纯幂等的计算/同步链路里,这两套重试 + 死信策略是不是分开的?各自的具体参数和踩坑点是什么?
赞 0 · 收藏 0 · 楼层 0
楼层
还没有人回复。