4. Broker核心参数调优
Kafka 的 Broker 本质上是一个网络服务程序。它要处理海量的客户端请求,还要把数据刷到磁盘。这两个任务如果混在一起干,系统很容易卡死。所以 Kafka 设计了一套线程模型,把网络接收和磁盘读写分开。
这套模型的核心参数有五个:num.network.threads、num.io.threads、socket.send.buffer.bytes、socket.receive.buffer.bytes、queued.max.requests。调好它们,Broker 的性能能翻倍。调不好,CPU 和内存都在浪费。
核心观点:网络线程负责“接活”,IO 线程负责“干活”。两者比例失调,就是性能瓶颈的开始。
4.1 线程模型全景图
先看一张图,理解数据是怎么流转的。我个人习惯在调优前,先把架构图画出来,这样心里有底。
这张图我画过很多次。每次给团队做培训,我都会指着请求队列说:这是整个模型的灵魂。没有它,网络线程和 IO 线程就是紧耦合,一卡全卡。
4.2 num.network.threads:网络线程数
这个参数控制 Broker 用于接收客户端请求的线程数量。说白了,就是前台接待员的人数。
| 参数名 | 默认值 | 建议范围 |
|---|---|---|
| num.network.threads | 3 | CPU 核数 × 2 到 × 3 |
调优原则:
- 网络线程是 CPU 密集型任务,主要做序列化/反序列化、协议解析、权限校验。
- 不要设置太多。线程多了,上下文切换开销反而吃掉性能。
- 我一般建议:
num.network.threads = CPU核数 × 2。比如 16 核机器,设 32。
我的经验:有一次在 32 核机器上,我把这个值设成了 128。结果 CPU 大量花在线程切换上,吞吐反而下降了 15%。后来压测发现,32 到 48 之间是最佳点。别贪多。
4.3 num.io.threads:IO 线程数
IO 线程负责真正的“脏活累活”——写磁盘、读磁盘、管理偏移量。这是 Broker 最宝贵的资源。
| 参数名 | 默认值 | 建议范围 |
|---|---|---|
| num.io.threads | 8 | CPU 核数 × 1 到 × 2 |
调优原则:
- IO 线程通常处理磁盘操作,会有等待时间(磁盘寻道、页缓存命中)。
- 如果磁盘是 SSD,IO 等待时间短,线程数可以少一些。
- 如果是机械盘,IO 等待时间长,线程数可以多一些,让 CPU 在等待时干别的活。
- 我个人习惯:SSD 场景用
num.io.threads = CPU核数,机械盘场景用CPU核数 × 1.5。
注意:IO 线程数不要超过磁盘数量太多。比如你只有 2 块数据盘,却设了 32 个 IO 线程,磁盘会成为瓶颈,线程都在排队等 IO。我曾经见过一个案例,IO 线程设了 64,磁盘利用率只有 40%,因为线程全在锁竞争上。
4.4 网络缓冲区:socket.send.buffer.bytes 与 socket.receive.buffer.bytes
这两个参数控制 Broker 端网络连接的 TCP 缓冲区大小。很多人忽略它们,但它们在跨机房场景下特别重要。
| 参数名 | 默认值 | 建议范围 |
|---|---|---|
| socket.send.buffer.bytes | 102400 (100KB) | 1MB - 4MB |
| socket.receive.buffer.bytes | 102400 (100KB) | 1MB - 4MB |
调优原则:
- 缓冲区越大,单次网络传输能打包的数据越多,吞吐越高。
- 但缓冲区太大,会占用内存。每个连接都有独立的缓冲区。
- 同机房低延迟场景,默认值 100KB 够用。跨机房高延迟场景,建议调到 1MB 以上。
关键点:这两个参数设置的是 Broker 端的 Socket 缓冲区。客户端也有对应的参数(send.buffer.bytes 和 receive.buffer.bytes),两边要配合调。我曾经遇到一个问题:Broker 端设了 2MB,客户端还是 100KB,结果吞吐上不去。两边都调到 2MB 后,问题解决。
4.5 queued.max.requests:请求队列深度
这个参数控制网络线程和 IO 线程之间的请求队列能积压多少请求。它是整个线程模型的“缓冲池”。
| 参数名 | 默认值 | 建议范围 |
|---|---|---|
| queued.max.requests | 500 | 500 - 2000 |
调优原则:
- 队列太短:网络线程处理得快,IO 线程处理得慢,网络线程会被阻塞,客户端感受到延迟抖动。
- 队列太长:积压请求太多,内存占用高,而且一旦 Broker 宕机,丢失的请求也多。
- 我一般建议:先保持默认 500。如果监控发现网络线程经常等待(等待时间 > 1ms),可以逐步增加到 1000 或 1500。
避坑指南:我曾经把 queued.max.requests 调到 5000,以为能扛住更大的突发流量。结果内存暴涨,GC 频繁,吞吐反而下降。后来压测发现,1500 左右是最佳点。记住:队列不是越大越好,够用就行。
4.6 参数组合调优实战
这几个参数不是孤立的。它们互相影响。我总结了一个调优步骤,你可以照着做:
- 先确定机器规格:CPU 核数、内存大小、磁盘类型(SSD/HDD)。
- 计算基础值:
num.network.threads = CPU核数 × 2num.io.threads = CPU核数(SSD)或 CPU核数 × 1.5(HDD)
- 设置缓冲区:
- 同机房:保持默认 100KB
- 跨机房:设为 1MB - 2MB
- 队列深度:先保持 500,压测后根据等待时间调整。
- 压测验证:用
kafka-producer-perf-test.sh和kafka-consumer-perf-test.sh跑负载,观察吞吐和延迟。
下面是一个典型的配置示例:
# 16核CPU,SSD磁盘,同机房场景
num.network.threads=32
num.io.threads=16
socket.send.buffer.bytes=1048576
socket.receive.buffer.bytes=1048576
queued.max.requests=1000
总结:调优的核心是找到网络线程和 IO 线程的平衡点。网络线程太快,IO 线程跟不上,队列积压。IO 线程太快,网络线程来不及接收,CPU 空闲。用监控工具(如 JMX、Prometheus)观察线程等待时间和队列深度,比凭感觉调参靠谱得多。