kafka 笔记四 kafka消费者 再均衡

1 什么是再均衡?
再均衡( Rebalance )本质上是一种协议,规定了一个消费组中所有消费者如何达成一致来分配订阅主题的每个分区。
比如某个消费组有 20 个消费组,订阅了一个具有 100 个分区的主题。正常情况下, Kafka 平均会为每个消费者分配5 个分区。这个分配的过程就叫再均衡。
 
2 什么时候再均衡?
再均衡的触发条件:
1. 组成员发生变更 ( 新消费者加入消费组组、已有消费者主动离开或崩溃了 )
2. 订阅主题数发生变更。如果正则表达式进行订阅,则新建匹配正则表达式的主题触发再均衡。
3. 订阅主题的分区数发生变更

 

3 如何进行组内分区分配?
三种分配策略:
RangeAssignor RoundRobinAssignor 以及 StickyAssignor
 
4 谁来执行再均衡和消费组管理?
Kafka
提供了一个角色: Group Coordinator来执行对于消费组的管理。
Group Coordinator—— 每个消费组分配一个消费组协调器用于组管理和位移管理。当消费组的第一个消费者启动的时候,它会去和Kafka Broker 确定谁是它们组的组协调器。之后该消费组内所有消费者和该组协调器协调通信。
 
5 如何确定coordinator
两步:
1. 确定消费组位移信息写入 __consumers_offsets 的哪个分区。具体计算公式: __consumers_offsets partition# = Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount) 注意: groupMetadataTopicPartitionCount由 offsets.topic.num.partitions 指定,默认是 50 个分区。
2. 该分区 leader 所在的 broker 就是组协调器。
 
6 Rebalance Generation
它表示 Rebalance 之后主题分区到消费组中消费者映射关系的一个版本,主要是用于保护消费组,隔离无效偏移量提交的。如上一个版本的消费者无法提交位移到新版本的消费组中,因为映射关系变了,你消费的或许已经不是原来的那个分区了。每次group 进行 Rebalance 之后, Generation 号都会加1,表示消费组和分区的映射关系到了一个新版本,如下图所示: Generation 1 group 3 个成员,随后成员2 退出组,消费组协调器触发 Rebalance ,消费组进入 Generation 2 ,之后成员 4 加入,再次触发
Rebalance,消费组进入Generation 3.
 
7 协议 (protocol)
kafka 提供了 5 个协议来处理与消费组协调相关的问题:
Heartbeat 请求: consumer 需要定期给组协调器发送心跳来表明自己还活着
LeaveGroup 请求:主动告诉组协调器我要离开消费组
SyncGroup 请求:消费组 Leader 把分配方案告诉组内所有成员
JoinGroup 请求:成员请求加入组
DescribeGroup请求:显示组的所有信息,包括成员信息,协议名称,分配方案,订阅信息等。通常该请求是给管理员使用
组协调器在再均衡的时候主要用到了前面
4 种请求。
 
8 liveness
消费者如何向消费组协调器证明自己还活着? 通过定时向消费组协调器发送 Heartbeat 请求。如果超过了设定的超时时间,那么协调器认为该消费者已经挂了。一旦协调器认为某个消费者挂了,那么它就会开启新一轮再均衡,并且在当前其他消费者的心跳响应中 添加 “REBALANCE_IN_PROGRESS” ,告诉其他消费者:重新分配分区
 
9 再均衡过程
再均衡分为 2 步: Join Sync
1. Join , 加入组。所有成员都向消费组协调器发送 JoinGroup 请求,请求加入消费组。一旦所有成员都发送了JoinGroup 请求,协调 i 器从中选择一个消费者担任 Leader 的角色,并把组成员信息以及订阅信息发给Leader
2. Sync Leader 开始分配消费方案,即哪个消费者负责消费哪些主题的哪些分区。一旦完成分配,Leader 会将这个方案封装进 SyncGroup 请求中发给消费组协调器,非 Leader 也会发SyncGroup请求,只是内容为空。消费组协调器接收到分配方案之后会把方案塞进SyncGroup的 response 中发给各个消费者
 
注意:在协调器收集到所有成员请求前,它会把已收到请求放入一个叫 purgatory( 炼狱 ) 的地方。然后是分发分配方案的过程,即SyncGroup 请求:
 
注意 :消费组的分区分配 方案在客户端执行 Kafka 交给客户端可以有更好的灵活性。 Kafka 默认提供三种分配策略:range round-robin sticky 。可以通过消费者的参数:partition.assignment.strategy 来实现自己分配策略。
 
10 消费组状态机
消费组组协调器根据状态机对消费组做不同的处理:
说明:
1. Dead :组内已经没有任何成员的最终状态,组的元数据也已经被组协调器移除了。这种状态响应各种请求都是一个response UNKNOWN_MEMBER_ID
2. Empty :组内无成员,但是位移信息还没有过期。这种状态只能响应 JoinGroup 请求
3. PreparingRebalance :组准备开启新的 rebalance ,等待成员加入
4. AwaitingSync :正在等待 leader consumer 将分配方案传给各个成员
5. Stable :再均衡完成,可以开始消费。
 
11 RangeAssignor
PartitionAssignor 接口用于用户定义实现分区分配算法,以实现 Consumer 之间的分区分配。消费组的成员订阅它们感兴趣的Topic 并将这种订阅关系传递给作为订阅组协调者的 Broker 。协调者选择其中的一个消费者来执行这个消费组的分区分配并将分配结果转发给消费组内所有的消费者。 Kafka 默认采用 RangeAssignor 的分配算法
RangeAssignor 对每个 Topic 进行独立的分区分配。对于每一个 Topic ,首先对分区按照分区 ID 进行数值排序,然后订阅这个Topic 的消费组的消费者再进行字典排序,之后尽量均衡的将分区分配给消费者。这里只能是尽量均衡,因为分区数可能无法被消费者数量整除,那么有一些消费者就会多分配到一些分区.
 
assign(topic, consumers) { 
// 对分区和Consumer进行排序 
    List<Partition> partitions = topic.getPartitions(); 
    sort(partitions); 
    sort(consumers); 
// 计算每个Consumer分配的分区数 
    int numPartitionsPerConsumer = partition.size() / consumers.size(); 
    // 额外有一些Consumer会多分配到分区 
    int consumersWithExtraPartition = partition.size() % consumers.size(); 
    // 计算分配结果 
    for (int i = 0, n = consumers.size(); i < n; i++) {
        // 第i个Consumer分配到的分区的
        index int start = numPartitionsPerConsumer * i + Math.min(i, consumersWithExtraPartition); 
        // 第i个Consumer分配到的分区数 
        int length = numPartitionsPerConsumer + (i + 1 > consumersWithExtraPartition ? 0 : 1); 
        // 分装分配结果     
        assignment.get(consumersForTopic.get(i)).addAll(partitions.subList(start, start + length)); 
    } 
}
RangeAssignor 策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。对于每一个Topic ,RangeAssignor策略会将消费组内所有订阅这个 Topic 的消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分区。
这种分配方式明显的一个问题是随着消费者订阅的 Topic 的数量的增加,不均衡的问题会越来越严重,比如上图中4 个分区 3 个消费者的场景, C0 会多分配一个分区。如果此时再订阅一个分区数为 4 的Topic,那么 C0 又会比 C1 C2 多分配一个分区,这样 C0 总共就比 C1 C2 多分配两个分区了,而且随着Topic的增加,这个情况会越来越严重。
字典序靠前的消费组中的消费者比较 贪婪
 
12 RoundRobinAssignor
RoundRobinAssignor 的分配策略是将消费组内订阅的所有 Topic 的分区及所有消费者进行排序后尽量均衡的分配(RangeAssignor 是针对单个 Topic 的分区进行排序分配的)。如果消费组内, 消费者订阅的Topic列表是相同的 (每个消费者都订阅了相同的 Topic ),那么分配结果是尽量均衡的(消费者之间分配到的分区数的差值不会超过1 )。 如果订阅的Topic列表是不同的 ,那么分配结果是不保证 尽量均衡” 的,因为某些消费者不参与一些 Topic 的分配。
 
相对于 RangeAssignor ,在订阅多个 Topic 的情况下, RoundRobinAssignor 的方式能消费者之间尽量均衡的分配到分区(分配到的分区数的差值不会超过1——RangeAssignor 的分配策略可能随着订阅的Topic 越来越多,差值越来越大)。
对于消费组内消费者订阅 Topic 不一致的情况:假设有两个个消费者分别为 C0 C1 ,有 2 TopicT1、 T2 ,分别拥有 3 2 个分区,并且 C0 订阅 T1 T2 C1 订阅 T2 ,那么 RoundRobinAssignor 的分配结果如下:
看上去分配已经尽量的保证均衡了,不过可以发现 C0 承担了 4 个分区的消费而 C1 订阅了 T2 一个分区,是不是把T2P0 交给 C1 消费能更加的均衡呢?
 
 
13 StickyAssignor
动机
尽管 RoundRobinAssignor 已经在 RangeAssignor 上做了一些优化来更均衡的分配分区,但是在一些情况下依旧会产生严重的分配偏差,比如消费组中订阅的Topic 列表不相同的情况下。 更核心的问题是无论是RangeAssignor ,还是 RoundRobinAssignor ,当前的分区分配算法都没有考虑上一次的分配结果 。显然,在执行一次新的分配之前,如果能考虑到上一次分配的结果,尽量少的
调整分区分配的变动,显然是能节省很多开销的
目标
从字面意义上看, Sticky 粘性的 ,可以理解为分配结果是带 粘性的
1. 分区的分配尽量的均衡
2. 每一次重分配的结果尽量与上一次分配结果保持一致
当这两个目标发生冲突时,优先保证第一个目标。第一个目标是每个分配算法都尽量尝试去完成的,而第二个目标才真正体现出StickyAssignor 特性的。
例如:
1.有 3 Consumer C0 C1 C2
2.有 4 Topic T0 T1 T2 T3 ,每个 Topic 2 个分区
3.所有 Consumer 都订阅了这 4 个分区
StickyAssignor的分配结果如下图所示(增加
RoundRobinAssignor 分配作为对比):
按照 Sticky 的方式:
仅对消费者 1 分配的分区进行重分配,红线部分。最终达到均衡的目的。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值