Client Rebalance 简介
什么是 Client Rebalance
在 Kafka 中,Client Rebalance 是指消费者组(Consumer Group)内的消费者实例重新分配订阅 partition 的过程。当组内成员变化、partition 数变化或订阅关系变更时,Kafka 会触发 Client Rebalance 以确保 partition 在客户端消费者的所有权尽可能均匀分布。
为什么需要 Client Rebalance
消费者组间的 client 实现动态负载均衡,其存在的本质原因是为了确保 partition 与消费者实例的分配关系能够随集群状态动态调整。
场景 1:扩容 client 实例
场景 2:缩容 client 实例
场景 3:client 实例故障
场景 4:topic 扩容
场景 5:topic 删除
Partition 分配算法
既然 Rebalance 的目标是为了让 partition 在众多的 client 中均匀分配,那么下面介绍一下 partition 的分配算法。
RangeAssignor
将 topic 的 partition 按顺序分组,依次分配给消费者,适用于单 topic 的分配场景。
核心原理
1. 单 topic 排序与分组
对每个 topic 的 partition 按编号升序排列(如 topic1 有 partition 0-5),按消费者数量计算每组 partition 数:partition 数 / 消费者数 = 基础 partition数,余数部分由前余数个消费者各多分配 1 个。
例:topic 有 5 个 partition,2 个消费者(C1、C2),计算为:
C1:partition 0, 1, 2(5/2 = 2 余 1,前 1 个消费者多拿 1 个)
C2:partition 3, 4
2. 多 topic 分配逻辑
对每个 topic 独立执行上述分配,可能导致同一消费者在不同topic中分配到过量partition。
优缺点
优点:
按顺序分配,便于理解和调试。
Rebalance 时分配逻辑稳定,便于预测。
缺点:
分配可能不均匀:当 partition 数不能被消费者数整除时,前余数个消费者会多拿 partition。
不适合多 topic,多 topic 场景下可能导致 “数据倾斜”:如消费者 A 负责 topic1 的 3 个 partition 和 topic2 的 3 个 partition,而消费者 B 仅负责 2 个partition。
RoundRobinAssignor
将所有 topic 的 partition 统一排序,按轮询方式依次分配给消费者,追求全局均衡。
核心原理
1. 全局 partition 排序
将所有订阅 topic 的 partition 按 topic + partition 升序排列(如 topic1-0, topic1-1, topic2-0, topic2-1 )。
2. 轮询分配机制
按消费者列表顺序(按消费者 ID 升序)依次分配每个 partition,类似 “发牌” 模式。
例:3 个消费者(C1, C2, C3)分配 6 个 partition,结果为:
C1: partition 0, 3
C2: partition 1, 4
C3: partition 2, 5
3. 配置
partition.assignment.strategy 需显式配置为 org.apache.kafka.clients.consumer.RoundRobinAssignor。优缺点
优点:
全局分配更均匀,避免单 topic Range 算法的倾斜问题。
支持跨 topic 的负载均衡,适合多 topic 消费场景。
缺点:
Rebalance 时可能触发大量 partition 迁移:当消费者数量变化时,所有 partition 需重新轮询分配,开销较大。
StickyAssignor
优先保留历史 partition 分配关系,仅在必要时调整 partition,减少 rebalance 导致的迁移开销
核心原理
1. “粘性” 分配策略
rebalance时,分配器会优先将之前分配的partition保留给原消费者,仅当partition必须重新分配时(如消费者离开)才调整。
例:消费者 C1 原有partition 0-2,新增消费者 C3 时,C1 可能仅释放partition 2,保留 0-1。
2. 与 Cooperative 协议的结合
在 Kafka 2.4 + 版本中,CooperativeStickyAssignor支持增量rebalance,分阶段迁移partition,避免全量迁移partition。
优缺点
优点:
大幅减少 rebalance 时的 partition 迁移量,降低应用中断时间。
缺点:
实现复杂度高,需跟踪历史分配状态(通过
ownedPartitions 字段)。自定义分配器需显式实现粘性逻辑,否则可能退化为旧协议。
若历史分配存在倾斜,粘性策略可能延续该倾斜(需手动干预或重启重置)。
对比三种均衡算法
维度 | RangeAssignor | RoundRobinAssignor | StickyAssignor |
分配粒度 | 单 topic 内的 partition 范围分组 | 全局 partition 轮询 | 优先保留历史分配,最小化迁移 |
均衡性 | 单 topic 内可能不均衡,多 topic 易倾斜 | 全局均衡性最佳 | 历史分配优先,可能延续旧倾斜 |
rebalance开销 | 高(全量重新分配) | 高(全量重新分配) | 低(仅必要 partition 迁移) |
状态感知 | 无状态 | 无状态 | 有状态(依赖历史分配记录) |
适用场景 | 单 topic、partition 数少、静态消费者组 | 多 topic、追求全局均衡、动态扩缩容 | 有状态应用、频繁 rebalance、需减少迁移的场景 |
Kafka 版本支持 | 全版本 | 全版本 | 旧版 StickyAssignor(≤2.3),新版CooperativeStickyAssignor(≥2.4) |
总结
RangeAssignor:适用于单 topic、消费者数量固定、对分配均匀性要求不高的场景
RoundRobinAssignor:适用于多 topic 消费、消费者数量动态变化、追求全局均衡的场景
StickyAssignor:优先用于 Kafka Streams、Kafka Connect 等有状态组件,减少 rebalance 导致的状态重建。
Rebalance 触发场景
consumer 成员变化
consumer 挂掉
consumer 扩容
consumer 缩容
topic 变化
topic 创建
topic 删除
partition 扩容
Rebalance 协议流程解析
Eager Rebalance 协议流程

ConsumerRebalanceListener
onPartitionsRevoked(在发送 join group 请求之前调用 )
onPartitionsAssigned(在收到 sync group 请求之后调用)
ConsumerPartitionAssigner
assign (client leader 调用该接口实现 partition 分配算法)
Eager Rebalance存在缺陷
1. Stop the world:消费断流,尤其在 partition 和 client 数量比较大的情况下比较明显。
2. partition 无效迁移:例如在单实例重启场景中 partition 迁移。
优化Eager Rebalance协议
Static Membership Rebalance协议
优化思路:降低触发 rebalance 的概率,提高触发 rebalance 的要求,主要优化单实例重启的场景。
优化方式:
1. 为每个实例增加一个
instance.id 的参数,在 k8s 上的一个实例重启之后该 instance.id 保持不变。2. 调整
session.timeout 参数,尽量调大 session.timeout 以实现实例在重启时 coordinator 感知不到实例没有发送心跳。3. 新启动的实例携带重启之前的
instance.id,coordinator 找到对应的 partitions 信息,返回给新启动的实例。

优化后触发 rebalance 的场景:
1. client 扩容
2. client leader 重新 rejoin
3. client 实例挂掉的时间超过 session timeout
4. client缩容
Cooperative Rebalance协议
优化思路:触发 rebalance 的条件保持不变,优化 rebalance 流程,尽量保持 partition 不发生迁移。

在一次 rebalance 过程中,对于一个 client 的 partition 来说,可以认为处理三种状态:
需要被 client 撤销
需要被 client 加入
保持不变
所以在 rebalance 过程中,对于保持不变的 partition 让其仍继续消费,然后先撤销 partition,再增加 partition。
优化方式:
1. 在 rebalance 之前不再撤销正在消费 partition。
2. 渐进式多轮 rebalance,“撤销旧 partition” 和 “分配新 partition” 两个阶段,消费者可在第一阶段继续处理已保留的 partition,减少服务中断。

从 Eager Rebalance 协议升级到 Cooperative Rebalance 协议
需要 consumer 修改配置重启2次:
1. 引入新协议同时保留旧的协议
1.1 修改 consumer 配置
partition.assignment.strategy = org.apache.kafka.clients.consumer.CooperativeStickyAssignor,org.apache.kafka.clients.consumer.RangeAssignor
注意:
新分配器的配置要放在列表第一位
1.2 重启 consumer,新实例发送的 joinGroup 请求包含新协议,但由于旧协议仍在列表中,消费者组会选择所有分配器共同支持的协议(此时仍为eager,因 RangeAssignor 仅支持 eager)。旧实例继续使用 eager 协议,新实例准备支持 cooperative 协议,但协议尚未切换。
2. 移除协议
从配置中删除旧的协议
partition.assignment.strategy = org.apache.kafka.clients.consumer.CooperativeStickyAssignor
对比 Static Membership 和 Cooperative Rebalance 协议
Static Membership | Cooperative | ||
优点 | 1. 流程简单 2. 从根本上不触发 rebalance 3. 兼容 StickyAssignor | 1. 不依赖单独的唯一 id 2. 感知历史分配结果,降低了 rebalance 期间抖动 | 2个协议不矛盾,可以同时使用 |
缺点 | 1. 需要提供instance.id 2. 调大 session timeout,可能导致容器挂掉时感知不明显 3. 不生效时退化为全局 rebalance | 1. 流程复杂 2. 需要使用 CooperativeStickyAssignor,依赖粘性分配算法 | |
Client Rebalance 在腾讯云的实践
查看当前 Partition 的均衡算法
您可以通过进入实例详情,查看 Consumer Group 页签,在列表中查看客户端均衡算法。

查看 partition 在 client 间的分配结果
前提:在代码中
KafkaConsumer对象一定要调用 subscribe 函数。当前使用 group test 来消费 2 个 topic,一个 topic: test1 有 10 个 partition,另外一个 topic: test2 有 5 个 partition。


按照 range 的分配算法后,发现第一个消费者消费 6 个 partition,第二个消费者消费 4 个 partition,第三个消费者消费 5 个 partition,如果改成RoundRobinAssignor 均衡算法后,结果如下:


发现 3 个 client 之间分配的 partition 数量更平均一些,每个 client 都分配5个 partition。
参考资料