已采纳
回答正文
回复
讨论正文
该问题的正文、回答与讨论。
我做过 10 年分布式,带团队跑过订单履约、账务对账两条 at-least-once 消费链路。Kafka 3.5(KRaft,3 broker),topic 16 分区、峰值 4k msg/s,消费组 8 实例,单条消息的业务处理分四步:
幂等键是 biz_id(订单号 + 事件类型),用唯一索引挡重复写业务表。这套方案在「整条消息重复投递」的场景下没问题,但处理进行中被 rebalance 踢出这个窗口,我们一直没有一个被验证过的工程方案。
去年一次 broker 扩容触发滚动重启,消费组 8 实例里有 3 个被踢出。事后从日志里捞到的数据:
复盘时发现:我们团队没有一张「中间态表」,所有状态都靠日志和 RPC 侧流水对出来,这在 8 实例 × 16 分区的规模下根本对不全。
session.timeout.ms(默认 45s → 120s):rebalance 窗口变宽,但被踢实例上正在处理的消息依然可能在 120s 内走到「半完成」,问题只是概率下降,没有消失。status=PROCESSING,完成后改 status=DONE。问题是:步骤 2、3 的中间态没有落任何地方,恢复 worker 看到 PROCESSING 也不知道该从哪一步续,只能整条重跑,于是又回到「WMS 扣两次」的坑。我不想要一篇「幂等的重要性」综述,想看到具体到表结构和判定规则的方案:
RECEIVED / DB_WRITTEN / WMS_CONFIRMED / LOGISTICS_CONFIRMED / NOTIFIED / DONE / ROLLED_BACK)?版本号还是状态机?乐观锁还是 CAS?恢复 worker 用 SELECT ... FOR UPDATE 还是 UPDATE ... WHERE status = ??DB_WRITTEN 状态时,怎么决定是「回滚业务表 + 冲正 WMS」还是「跳过步骤 1、继续执行步骤 2」?哪些状态适合回滚、哪些适合前滚?判定依据是什么(RPC 侧是否有查询接口?业务表是否可逆?)?我引用三条我觉得相关的站内内容,希望回答者能看到上下文:
期待看到具体表结构、状态枚举、判定伪代码,以及真实踩坑后的修正方案。