已采纳
回答正文
回复
讨论正文
该问题的正文、回答与讨论。
团队 8 人维护订单 → 库存 → 支付链路,消费端"至少一次 + 幂等"已做好(幂等表 + 唯一键,TTL 72h),这块不再赘述。今天想聊的是生产端一个更隐蔽的卡点:
本地 DB 事务(insert 订单行 + update 库存)已 COMMIT,但紧接着的 MQ send 因 broker 端 Full GC 停顿 / 网络抖动 / 连接半开而失败 → 下游永远收不到这条消息。
这不是"重复消费"问题,而是"消息根本没发出去"——消费端再健壮也救不了。
直接 try-catch 重试 send ×3:压测时 broker 端一次 600ms 的 Full GC 就能让 3 次 send 全部超时;更糟的是第 2 次 send 可能已到达 broker 但响应丢了,导致重复投递(消费端幂等兜底了,但生产端日志完全看不出是"没发出去"还是"发了但响应丢了")。
本地消息表 + 定时扫描(5s 间隔,每批 500 条):
SELECT ... WHERE status='pending' ORDER BY gmt_create LIMIT 500 在 12 万行 pending 上频繁扫描);RocketMQ 事务消息(half message + 回查):
希望看到真实线上参数 + 踩坑时间线(比如"我们 2023 年大促用了方案 B,积压 8 万条时 P99 飙到 xx ms,后来切到方案 C"),而不是教科书式的"建议用 outbox 模式"。如果你们团队踩过我上面列的任意一个坑,欢迎贴细节;如果你们走了完全不同的路(比如把 MQ 换成 DB 轮询 / 事件溯源),也请讲讲取舍。
引用两条消费端侧重的讨论作为背景对照:
- 讨论帖 #620《消息重试的退避策略与死信队列:你们到底按什么规则决定"重试几次、退多久、最后落哪儿"》——消费端重试策略已经讨论得很充分了,生产端"发不出去"这一侧反而缺少正面交锋。
- 讨论帖 #82《消费失败后的"重试 + 死信 + 人工补偿",你们团队到底是怎么分工的》——同样聚焦消费端,生产端原子性是互补的另一半。