1. 生产者核心参数解析:从原理到实战
各位同学好,我是老张。今天咱们来聊聊 Kafka 生产者的那些核心参数。说实话,很多初学者一上来就照着网上的配置抄,结果生产环境一跑就出问题。我见过太多因为参数没调好导致的丢数据、延迟高、甚至 OOM 的案例了。
这一章,我会把每个参数掰开揉碎了讲清楚。不光说它是什么,更重要的是——什么时候该用,什么时候不该用。
核心观点:没有万能的参数配置,只有适合你场景的参数组合。理解了每个参数背后的设计意图,你才能做出正确的选择。
1.1 acks:数据可靠性的第一道防线
acks 可能是生产者最重要的参数,没有之一。它决定了生产者要等待多少个副本确认,才算消息发送成功。
| 取值 | 含义 | 可靠性 | 延迟 | 典型场景 |
|---|---|---|---|---|
acks=0 |
不等待任何确认 | 最低 | 最低 | 日志采集、监控指标(可丢数据) |
acks=1 |
等待 leader 确认 | 中等 | 中等 | 大部分业务场景(默认值) |
acks=all 或 -1 |
等待所有 ISR 副本确认 | 最高 | 最高 | 金融、交易、订单等关键数据 |
我个人习惯,只要涉及钱或者用户核心数据,一律用 acks=all。你可能觉得延迟高了点,但比起丢数据后的修复成本,这点延迟完全可以接受。
避坑指南:我曾经在一个日志采集系统里用了 acks=0,结果某次 broker 宕机,丢了将近 10 分钟的数据。虽然日志丢了可以重采,但排查问题花了一整天。所以,acks=0 只适合那些丢了也无所谓的场景。
1.2 retries:失败重试的艺术
retries 控制的是发送失败时的重试次数。默认值是 Integer.MAX_VALUE,但别高兴太早——光有重试次数还不够,你还得配合 retry.backoff.ms 一起用。
为什么?因为如果重试间隔太短,broker 还没恢复,你重试多少次都是白费。我一般设置 retries=3,配合 retry.backoff.ms=300,这样既不会无限重试,也不会把 broker 压垮。
小技巧:如果你用了 enable.idempotence=true(幂等性),retries 会自动变成 Integer.MAX_VALUE。这是 Kafka 为了保证 exactly-once 语义做的设计,别慌。
1.3 batch.size 与 linger.ms:吞吐量的黄金搭档
这两个参数要放在一起讲,因为它们共同决定了消息的批量发送行为。
- batch.size:每个批次的最大字节数,默认 16KB。不是越大越好,也不是越小越好。
- linger.ms:发送前等待更多消息加入批次的时间,默认 0ms。
你想想看,如果 linger.ms=0,那消息一来就发,batch.size 再大也没用。反过来,如果 linger.ms 设得太大,延迟就上去了。
我一般这样配:
// 高吞吐场景
props.put("batch.size", 32768); // 32KB
props.put("linger.ms", 10); // 10ms
// 低延迟场景
props.put("batch.size", 16384); // 16KB
props.put("linger.ms", 0); // 立即发送
说白了,这就是个 trade-off。你要吞吐量,就得忍受一点延迟;你要低延迟,吞吐量就会下降。
1.4 buffer.memory:别让内存成为瓶颈
buffer.memory 是生产者用于缓存待发送消息的内存池大小,默认 32MB。这个参数经常被忽略,但坑特别多。
我记得有一次,线上突然报 org.apache.kafka.common.errors.TimeoutException,排查了半天才发现是 buffer.memory 太小,消息积压把内存池撑爆了。当时业务量翻了一倍,但配置没跟着改。
怎么算?一个简单的公式:
buffer.memory ≈ 峰值 TPS × 平均消息大小 × 峰值持续秒数
举个例子,如果你的峰值 TPS 是 10000,平均消息 1KB,峰值持续 10 秒,那至少需要 10000 × 1KB × 10 = 100MB。再加上一些余量,我建议设到 128MB 或 256MB。
注意:buffer.memory 设得太大也不行。如果生产者挂了,这部分未发送的数据就全丢了。所以,结合 block.on.buffer.full(已废弃,现在用 max.block.ms)一起考虑。
1.5 max.request.size:消息大小的天花板
这个参数控制的是单个请求的最大字节数,默认 1MB。如果你要发送大消息,比如图片、日志文件片段,就得调大它。
但要注意,max.request.size 不仅影响生产者,还影响 broker 端的 message.max.bytes。两边必须保持一致,否则 broker 会拒绝接收。
// 生产者端
props.put("max.request.size", 10485760); // 10MB
// broker 端(server.properties)
message.max.bytes=10485760
replica.fetch.max.bytes=10485760 // 副本同步也要调大
我曾经犯过一个错:只改了生产者,没改 broker,结果消息发出去就被 broker 拒了,日志里全是 RecordTooLargeException。嗯,这种低级错误,犯一次就够了。
1.6 compression.type:用 CPU 换带宽
compression.type 指定消息的压缩算法,可选值有 none、gzip、snappy、lz4、zstd。默认是不压缩。
| 算法 | 压缩比 | CPU 开销 | 推荐场景 |
|---|---|---|---|
| gzip | 高 | 高 | 带宽瓶颈、消息量大 |
| snappy | 中等 | 低 | 追求吞吐量、CPU 敏感 |
| lz4 | 中等 | 低 | 平衡场景(推荐) |
| zstd | 最高 | 中等 | Kafka 3.0+、追求极致压缩 |
我个人最常用 lz4,压缩比不错,CPU 开销也低。如果你用的是 Kafka 3.0 以上版本,zstd 也是个好选择,压缩比能到 gzip 的 1.5 倍以上。
经验之谈:压缩是在生产者端完成的,broker 和消费者端会自动解压。所以,只要生产者 CPU 有余量,建议开启压缩。尤其是文本类消息,压缩比非常可观。
1.7 核心参数关系图
说了这么多,你可能有点晕。别急,我画了张图,把这些参数的关系串起来:
1.8 实战配置模板
最后,给你两个可以直接用的配置模板。一个是高可靠场景,一个是高吞吐场景。
// ===== 高可靠配置(金融、交易场景) =====
Properties props = new Properties();
props.put("bootstrap.servers", "kafka1:9092,kafka2:9092,kafka3:9092");
props.put("acks", "all");
props.put("retries", 3);
props.put("retry.backoff.ms", 300);
props.put("enable.idempotence", true); // 幂等性,防止重复
props.put("batch.size", 16384);
props.put("linger.ms", 5);
props.put("buffer.memory", 134217728); // 128MB
props.put("max.request.size", 1048576); // 1MB
props.put("compression.type", "lz4");
props.put("max.block.ms", 3000); // 阻塞超时
// ===== 高吞吐配置(日志、监控场景) =====
Properties props = new Properties();
props.put("bootstrap.servers", "kafka1:9092,kafka2:9092,kafka3:9092");
props.put("acks", "1");
props.put("retries", 2);
props.put("retry.backoff.ms", 200);
props.put("batch.size", 65536); // 64KB
props.put("linger.ms", 20);
props.put("buffer.memory", 268435456); // 256MB
props.put("max.request.size", 5242880); // 5MB
props.put("compression.type", "snappy");
props.put("max.block.ms", 1000);
嗯,这一章的内容就到这。记住,参数配置不是一劳永逸的事。业务量变了、数据特征变了,参数就得跟着调。多观察、多测试,慢慢你就能找到最适合自己场景的那组配置。