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 核心参数关系图

说了这么多,你可能有点晕。别急,我画了张图,把这些参数的关系串起来:

Kafka 生产者核心参数关系图 生产者核心参数关系图 Kafka Producer buffer.memory 内存池大小(默认 32MB) batch.size + linger.ms 批次大小与等待时间 compression.type acks + retries + max.request.size 参数配置顺序 1. 先确定可靠性(acks) 2. 再调吞吐量(batch/linger) 3. 最后优化资源(压缩/内存) 常见误区 ❌ 只调一个参数 ✅ 参数之间要联动调整

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);

嗯,这一章的内容就到这。记住,参数配置不是一劳永逸的事。业务量变了、数据特征变了,参数就得跟着调。多观察、多测试,慢慢你就能找到最适合自己场景的那组配置。