Kafka Consumer Rebalance

0.8.2.2之前版本版本:

  Kafka的Consumer Rebalance方案是基于Zookeeper的Watcher来实现的。每个consumer group(cg)在zk下都维护一个”/consumers/[group_name]/ids”路径,在此路径下,使用临时节点记录属于此cg的消费者的Id,该Id信息由对应的consumer在启动时创建。还有两个与ids同一级别的节点,他们分别是:owners节点,记录了分区与消费者的对应关系(即:Topic A的Partition 0在Group G上实际由哪个Id的消费者负责消费);offset节点,记录了此cg在某个分区上的消费位置(即:Group G对Topic A的Partition 0实际现在消费到了第几的offset)。每个Broker、Topic以及分区在zk上也都对应一个路径:

/brokers/ids/[broker_id]:记录了该broker的host、port信息,同时还记录了分配在此Broker上的Topic的分区列表。
/brokers/topics/[topic_name]:记录了每个partition的leader、isr等信息。
/brokers/topics/[topic_name]/partitions/[partition_num]/stat:记录了当前leader、选举epoch等信息


Kafka Consumer Rebalance_第1张图片
image.png

    每个consumer都分别在”/consumers/[group_name]/ids”和”/brokers/ids”路径上注册一个Watcher。当”/consumers/[group_name]/ids”路径的子节点发生变化时,表示consumer group中的消费者出现了变化;当”/brokers/ids”路径的子节点发生变化时,表示Broker出现了增减。这样,通过Watcher,每个消费者就可以监听Consumer Group和Kafka集群的状态变化了。

Kafka Consumer Rebalance_第2张图片
image.png
Consumer rebalacne算法:
1. 将目标 topic 下的所有 partirtion 排序,存于PT
2. 对某 consumer group 下所有 consumer 排序,存于 CG,第 i 个consumer 记为 Ci
3. N=size(PT)/size(CG),向上取整
4. 解除 Ci 对原来分配的 partition 的消费权(i从0开始)
5. 将第i*N到(i+1)*N-1个 partition 分配给 Ci  
控制策略:
1. 在/consumers/[consumer-group]/下注册id
2. 设置对/consumers/[consumer-group] 的watcher
3. 设置对/brokers/ids的watcher
4. zk下设置watcher的路径节点更改,触发consumer rebalance

但由于验证依赖于zk集群,有两个比较严重的问题:

  • 羊群效应(Herd Effect):一个被Watch的zk节点变化,导致大量的watcher通知需要被发送给客户端,这会导致在通知期间其他操作的延迟。一般出现这种情况的主要原因就是没有找到客户端真正关注的点,也就是滥用Watcher的一种场景。例如,g1是Consumer Group A(G_A)中的一员,则他会去关注”/consumers/G_A/ids”路径的子节点变化,假设该Consumer Group A中还有一个成员g2,这里需要注意的是,Kafka允许同一cg中的两个成员,分别用于sub不同的topic,例如g1订阅的topic 1的消息,g2订阅的是topic 2上的消息,由于g1, g2都归属于G_A,则他们都会关注”/consumers/G_A/ids”,当g2退出时,路径”/consumers/G_A/ids”下关于g2的节点将消失,此时,g1将感知到节点变化,进而进入rebalance状态,然而g1,g2虽然同属于同一个group,但实际上他们是订阅着不同的topic,两者在工作上并无交集,因此,g2的退出,引起g1进行rebalance实际上是没有意义的,而在这个方案中,g1却无辜躺枪。

  • 脑裂(Split Brain):每个Consumer都是通过zk中保存的元数据来判断group中各其他成员的状态,以及broker的状态,进而分别进入各自的Rebalance,执行各自的Rebalance逻辑。由于zk是只保证”最终一致性”,不保证在状态切换过程中个连接客户端看到的一致性(“Simultaneously Consistent Cross-Client Views”),这带来的问题就是,不同的Consumer在同一时刻可能连接在不同的zk服务器上,看到的元数据就可能不一样,基于不一样的元数据,执行Rebalance就会产生不一致(冲突)的Rebalance结果,Rebalance的冲突,会到导致consumer的rebalance失败。

常见现象:

    目前有三种方式会触发rebalance,其一Topic与partition映射表发生变化;其二同一订阅组中消费节点数发生变化,其三zk会话过期 。
    前两种直接就做rebalance操作了,最后一种消费节点重新注册临时znode到zk上,然后再做rebalance操作,rebalance失败会导致消费节点下线,其他活跃消费节点也无法分配到分片数据,其实质原因是消费节点rebalance失败下线时并没有从zk的Consumer Group下删除自身临时节点,而每个消费节点的分片数据是根据Consumer Group下数量按照计算规则分配的,所以活跃消费节点无法分配到分片数据。
==========================
    归根到底就是 原有consumer没有释放掉上次分配的partition,导致了下次rebalance的时候发现分配的partition还没有被释放,所以这个consumer就报出ConsumerRebalanceFailedException;如果等到释放了该partition,并再次触发了rebalance,则可以分配成功。

针对业务经常遇到rebalance failed:
  1. 检查代码和配置(确保 rebalance.max.retries * rebalance.backoff.ms > zookeeper.session.timeout.ms ),并重启
  2. 检查业务系统,有无出现OOM等情况(这种经常会导致所有的partition同时不消费)
  3. 检查zookeeper上节点状况,如ids、owner正常,则手动new consumer 触发一下rebalance,如果不是线程卡死造成资源占用等情况,应该就回成功消费
  4. 如果还是rebalance失败,是因为资源没有被释放,只能重启下业务的consumer进程

你可能感兴趣的:(Kafka Consumer Rebalance)