4. 生产者端故障:消息发送超时、Leader选举导致的生产失败、acks配置不当引发的数据丢失

生产端出问题,其实比消费端更隐蔽。消费端挂了你会立刻发现没数据,但生产端挂了——嗯,业务方可能还在继续发消息,只是全丢了。今天我们就聊三个最常见的坑:发送超时、Leader选举期间的失败、以及acks配置不当导致的数据丢失。

核心观点:生产端故障排查,本质上是「网络 + 元数据 + 确认机制」三个维度的博弈。任何一个环节出问题,消息都可能无声无息地消失。

4.1 消息发送超时:不只是网络慢

很多人一看到「TimeoutException」,第一反应就是「网络延迟高」。其实不然。我遇到过好几次,网络延迟只有2ms,但生产端就是疯狂超时。

为什么会这样?

Kafka生产端的超时,由两个参数共同决定:

参数 默认值 含义
request.timeout.ms 30000 等待Broker响应的最长时间
delivery.timeout.ms 120000 从发送到确认的总时间上限(包含重试)

注意,delivery.timeout.ms 是总时间。如果你设置了重试3次,每次重试间隔1秒,那光重试就花了3秒。如果 delivery.timeout.ms 设得太小,重试还没跑完,时间就到了。

我的习惯:把 delivery.timeout.ms 设为 request.timeout.ms 的 3-4 倍。这样重试有空间,又不会让客户端死等。

还有一个容易被忽略的点:缓冲区满了也会超时。生产端内部有个 buffer.memory,默认32MB。如果发送速度远大于Broker处理速度,缓冲区被占满,新消息就会阻塞。阻塞时间超过 max.block.ms(默认60秒),直接抛异常。

我曾经排查过一个案例:业务方每秒发送10万条消息,但目标Topic只有3个分区。结果缓冲区瞬间打满,生产端疯狂超时。解决方案很简单——增加分区数,或者调大 buffer.memory。

4.2 Leader选举导致的生产失败

这个坑我踩过不止一次。Leader选举期间,生产端会收到 NotLeaderForPartitionException 或 LeaderNotAvailableException。

正常流程是这样的:

  1. 生产端从元数据请求中拿到Leader信息
  2. 生产端向Leader发送消息
  3. Leader挂了,Controller开始选举新Leader
  4. 选举期间,生产端还在往旧Leader发消息——失败
  5. 生产端刷新元数据,拿到新Leader地址
  6. 继续发送

问题出在第4步到第5步之间。如果生产端没有配置重试,或者重试次数不够,消息就丢了。

避坑指南:我曾经见过一个生产环境,retries 设成了0。Leader切换时,所有正在发送的消息全部失败。业务方反馈「数据少了20%」,查了半天才发现是这个原因。

我建议的配置:

props.put("retries", Integer.MAX_VALUE);
props.put("retry.backoff.ms", 500);
props.put("max.in.flight.requests.per.connection", 5);

注意 max.in.flight.requests.per.connection 这个参数。如果设为1,可以保证消息顺序,但吞吐量会下降。如果设为大于1,重试可能导致消息乱序。嗯,这里要权衡。

还有一个细节:元数据过期时间。默认 metadata.max.age.ms 是5分钟。如果Leader频繁切换,生产端可能还在用旧的元数据。我一般把它降到30秒,让生产端更及时地感知集群变化。

4.3 acks配置不当引发的数据丢失

acks 是生产端最关键的参数,没有之一。它决定了「消息到底算不算发送成功」。

acks值 行为 风险
0 发出去就算成功,不等待任何确认 网络闪断、Broker宕机都会丢数据
1 等待Leader写入本地日志后返回成功 Leader宕机但Follower还没同步,数据丢失
all(或-1) 等待所有ISR副本都写入后返回成功 延迟高,但数据最安全

你想想看,很多公司为了追求吞吐量,把 acks 设为0或1。吞吐量确实上去了,但数据丢失的风险也上去了。

我记得有一次帮客户排查数据丢失问题。他们的生产端 acks=1,Leader 节点突然宕机。Follower 还没来得及同步最后一批消息,结果新 Leader 上任后,那些消息就永远消失了。

我的建议:金融、交易类场景,必须用 acks=all。日志、监控类场景,可以接受少量丢失,用 acks=1。至于 acks=0——除非你能接受任意数据丢失,否则别碰。

但 acks=all 也不是万能的。它依赖 ISR(In-Sync Replicas)机制。如果 ISR 里只剩一个副本(比如其他副本都挂了),那 acks=all 其实退化成 acks=1。所以还要配合 min.insync.replicas 使用。

# Topic级别配置
min.insync.replicas=2

# 生产端配置
props.put("acks", "all");

这样,如果 ISR 少于2个,生产端会收到 NotEnoughReplicasException,而不是静默丢数据。

4.4 一张图看懂生产端故障链路

下面这张图,把三个故障场景串起来了。你对照着看,排查思路会清晰很多。

生产者端故障排查链路图 生产者端故障排查链路 生产者 Producer ① 消息发送超时 ② Leader选举失败 ③ acks配置不当 request.timeout.ms 过小 delivery.timeout.ms 耗尽 buffer.memory 打满 retries 配置不足 元数据未及时刷新 max.in.flight 导致乱序 acks=0 无确认 acks=1 Leader宕机丢数据 min.insync.replicas 未设置 排查方法:日志 + 指标 + 参数检查 定位根因,修复配置

4.5 实战排查清单

遇到生产端故障,我一般按这个顺序查:

  1. 看日志:搜索 TimeoutException、NotLeaderForPartitionException、NotEnoughReplicasException
  2. 看指标:生产端指标里,重点关注 record-error-rate、request-latency-avg、buffer-available-bytes
  3. 看配置:确认 acks、retries、delivery.timeout.ms 是否合理
  4. 看集群:检查 Leader 是否频繁切换、ISR 是否完整

一个小技巧:在测试环境故意制造故障。比如停掉一个Broker,看看生产端会不会丢数据。提前暴露问题,比上线后补救强得多。

嗯,生产端的问题,说白了就是「发出去的消息到底有没有被安全保存」。理解了这一点,排查思路就清晰了。超时是时间问题,Leader选举是元数据问题,acks是确认机制问题——三个维度,逐个击破。