批量拉 500 条一起处理,第 37 条报错,整批被重新入队,前面已经成功的那些又被消费一遍。日志里能对上时间点,这种反而好排。

批量处理没有逐条隔离失败,一个异常让整批事务回滚,重投的时候前面成功的消息又跟着执行了一次。这一点先确认清楚,后面就不用反复试。

for (Message m : batch) {
  try { handle(m); ack(m); }
  catch (Exception e) { nack(m, false, false); }
}

「批量消费失败重投」排查到最后往往是很小的一处差异,前面那些复杂猜测可以先放一放。先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。

别只看报错的最后一行,往上看几行,真正的起因通常在上面。同类问题如果隔一段时间又出现,多半是某个定时任务或重启流程把改动冲掉了。

处理上按这个顺序:改成逐条处理并记下失败下标 只重投失败的那几条;重投前用业务唯一键做一次去重 避免重复入账;连续失败超过阈值就转补偿队列 不阻塞后面的消息。

结果对不对很好判断,重投的只有失败那几条,已经成功的消息不再重复出现。

作者 9823189