什么是 Kafka 的重平衡机制?
结论:重平衡(Rebalance)是消费组内"分区所有权"的重新分配过程:组协调器(GroupCoordinator)收集成员、选出组 Leader、由组 Leader 计算分配方案,再同步给所有成员。它保证"一个分区只被一个消费者消费",代价是分配期间消费暂停、位点提交可能混乱、消息可能重复。
一、两种协议的执行差异
Eager(旧协议,Range/RoundRobin/Sticky)
Trigger ──> 所有成员 revoke 全部分区 ──> JoinGroup ──> SyncGroup ──> 恢复消费
▲ 全组停摆,分区越多停得越久
Cooperative(增量协作,CooperativeSticky)
Trigger ──> JoinGroup ──> SyncGroup(只下发新增分区)
──> 成员仅撤销"不再属于自己"的分区 ──> 再一轮 SyncGroup 完成迁移
▲ 未受影响的分区持续消费,停摆范围小
| 对比项 | Eager | Cooperative |
|---|---|---|
| 停摆范围 | 全组 | 仅迁移中的分区 |
| 轮次 | 1 轮 | 通常 2 轮 |
| 重复消费 | 多 | 少 |
| 配置 | RangeAssignor 等 | CooperativeStickyAssignor |
二、触发条件
| 触发源 | 具体场景 | 是否可避免 |
|---|---|---|
| 成员加入 | 扩容消费者、发布重启 | 用静态成员可避免重启触发 |
| 成员离开 | 缩容、进程正常退出 | 计划内操作可控 |
| 心跳超时 | GC 停顿、网络抖动、线程卡死 | 调大 session.timeout.ms、排查 GC |
| 处理超时 | 单次 poll 间隔超过 max.poll.interval.ms | 减小 max.poll.records、异步化处理 |
| 订阅变化 | 正则订阅匹配到新 Topic | 避免用正则订阅核心业务 |
| 分区数变化 | Topic 扩容分区 | 规划阶段预留分区 |
三、重平衡对业务的影响
- 消费暂停:Eager 协议下全组停止拉取,分区多、成员多时可能持续数秒到数十秒;
- 重复消费:已处理但未提交位点的消息会被新 owner 重新拉取;
- 位点回退风险:撤销分区时若提交了旧位点,可能覆盖新进度(旧协议常见坑);
- 延迟抖动:重平衡常与发布、扩容同时发生,容易被误判为"消息延迟"。
四、减少重平衡的配置
# 消费者端
session.timeout.ms=45000 # 心跳超时,需大于 3 倍 heartbeat
heartbeat.interval.ms=3000 # 心跳间隔
max.poll.interval.ms=300000 # 两次 poll 的最大间隔
max.poll.records=200 # 单批条数,控制处理时长
partition.assignment.strategy=org.apache.kafka.clients.consumer.CooperativeStickyAssignor
group.instance.id=order-consumer-1 # 静态成员:重启不触发重平衡
enable.auto.commit=false
group.instance.id 是关键:配置静态成员后,实例重启在 session.timeout.ms 内重新加入,协调器认为还是同一个成员,不会触发全组重平衡。滚动发布场景收益非常明显。
五、监控与排查
# 观察组成员、分配、LAG
kafka-consumer-groups.sh --bootstrap-server 10.0.0.31:9092 \
--describe --group order-group
# 组状态:Stable / PreparingRebalance / CompletingRebalance / Dead
kafka-consumer-groups.sh --bootstrap-server 10.0.0.31:9092 \
--describe --group order-group --state
日志里出现 (Re)join group、Preparing to rebalance group、Revoking previously assigned partitions 就说明正在重平衡。频繁出现通常意味着成员不稳定:GC 停顿、poll 处理过慢、或者实例在反复重启。
六、常见追问
- 重平衡会丢消息吗? 不会因为重平衡本身丢,但"处理未完成就提交位点"会丢;"处理完成未提交"会重复。本质仍是提交时机。
- 消费者比分区多会怎样? 多出的消费者分不到分区,空转但仍是组成员,一旦有成员退出就会接手,属于热备。
- 重平衡期间生产会受影响吗? 不会。生产端只与分区 Leader 交互,重平衡只发生在消费侧。
- 怎么做到"发布不触发重平衡"? 静态成员 +
CooperativeStickyAssignor+ 合理的session.timeout.ms,并保证单批处理时间远小于max.poll.interval.ms。
小结
- 重平衡是消费组分区所有权的再分配,由组协调器驱动、组 Leader 计算方案。
- 触发源分三类:成员变化、心跳/处理超时、订阅或分区变化。
- Eager 全组停摆,Cooperative 增量迁移,新项目应直接用
CooperativeStickyAssignor。 - 静态成员 + 合理的超时参数是减少重平衡最有效的手段。