rebalance中文含义为再平衡。它本质上是一组协议,它规定了一个 consumer group 是如何达成一致来分配订阅 topic 的所有分区的。比方说Consumer Group A 有3个consumer 实例,它要消费一个拥有6个分区的topic,每个consumer消费2个分区,这就是rebalance。
rebalance是相对于consumer group 而言,每个consumer group会从kafka broker中选出一个作为组协调者(group coordinator)。coordinator负责对整个consumer group的状态进行管理,当有触发rebalance的条件发生时,促使生成新的分区分配方案。
rebalance触发的条件有三个:
其实无论哪种触发条件,我们可以发现根本原因还是因为topic 中partition或者consumer实例发生了变更。
分区分配策略决定了将topic中partition分配给consumer group中consumer实例的方式。
可以通过消费者客户端参数partition.assignment.strategy来设置消费者与主题之间的分区分配策略
kafka新版本提供了三种rebalance分区分配策略:
range 分配策略的原理是按消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能平均的分配给所有的消费者。
假设 n = 分区数/消费者数量,m= 分区数%消费者数量,那么前m个消费者每个分配n+1个分区,后面的(消费者数量-m)个消费者每个分配n个分区。
round-robin分配策略是将消费者组内所有主题的分区按照字典序排序,然后通过轮询的方式逐个将分区一次分配给每个消费者。
sticky分配策略是从0.11.x版本开始引入的分配策略,它主要有两个目的:
(1)分区的分配要尽可能均匀。
(2)分区的分配尽可能与上次分配的保持相同。
当两者发生冲突时,第一个目标优于第二个目标。sticky具体实现要比上面两种要复杂的多。
我们以一个具体的例子来说明。
假设消费者组内有3个消费者(C0、C1、C2),他们都订阅了4个主题(t0、t1、t2、t3),并且每个主题有两个分区。也就是说,整个消费者组订阅了t0p0、t0p1、t1p0、t1p1、t2p0、t2p1、t3p0、t3p1 8个分区。最终的分配结果为
消费者C0:t0p0、t1p1、t3p0
消费者C1:t0p1、t2p0、t3p1
消费者C2:t1p0、t2p1
这看上去似乎与round robin分配策略相同,事实上并不是这样。假设此时C1脱离了消费者组,那么消费者组就会执行rebalance,进而消费分区会重新分配。如果采用round robin策略,那么此时的分配结果如下
消费者C0:t0p0、t1p0、t2p0、t3p0
消费者C2:t0p1、t1p1、t2p1、t3p1
如果采用sticky分配策略,那么分配结果为
消费者C0:t0p0、t1p1、t3p0、t2p0
消费者C2:t1p0、t2p1、t0p1、t3p1
可以看到分配结果中保留了上一次分配中对消费者C0和C2的所有的分配结果,并将原来的消费者C1的负担分配给了剩余的两个消费者C0和C1,最终C0和C2的分配还保持了平衡。
前面提到。rebalance本质上是一组协议,consumer group和coordinator使用这组协议共同完成consumer group的rebalance。kafka新版本提供了下面5个协议来处理Rebalance
rebalance 过程中, coordinator 要处理 consumer 发过来的 JoinGroup 和SyncGroup 请求 。当consumer 主动离组时会发送 LeaveGroup 请求给 coordinator
在成功 rebalance 之后,组内所有 consumer 都需要定期地向 coordinator 发送 Heartbeat 请求,而每个 consumer 也是根据 Heartbeat 请求的响应中是否包含 REBALANCE_IN_PROGRESS判断当前 group 开启了新一轮 rebalance。
consumer group在执行rebalance之前必须首先确认coordinator在哪个broker上。并创建与该broker通信的socket连接。确定 coordinator 的算法与确定 offset 被提交到consumer offsets 目标分区的算法是相同的 算法如下:
成功连接 coordinator 之后便可以执行 rebalance 操作, 目前 rebalance 主要分为两步:加入组和同步更新分配方案