Kafka科普系列 | 原来Kafka中的选举有这么多?

9,125 阅读6分钟

面试官在考查你Kafka知识的时候很可能会故弄玄虚的问你一下:Kafka中的选举时怎么回事?除非问你具体的哪种选举,否则问这种问题的面试官多半也是对Kafka一知半解,这个时候就是“弄死”他的时候。当然如果你没有一定的知识储备,那么就是你被“弄死”的时候。

一般问这个问题,那么他肯定知道其中的一种,比如分区leader的选举。所谓分区leader的选举就是当ISR中的leader副本歇菜了,再重新选举一个的过程。对于这个问题就是你反客为主的机会,因为Kafka中的选举有多处,可不止分区leader的选举这一处,就算指明问分区leader的选举,那么也需要分4种情况具体分析。而这里面的细节是大多数人不甚明了的。

Kafka中的选举大致可以分为三大类:控制器的选举、分区leader的选举以及消费者相关的选举,这里还可以具体细分为7个小类。我们一一来过一下,本文只是简单罗列下大致的内容,至于内部具体的细节逻辑就需要靠读者自己去探索啦。虐人还是被虐就靠你的自驱力了。

控制器的选举

在Kafka集群中会有一个或多个broker,其中有一个broker会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态等工作。比如当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本。再比如当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。

Kafka Controller的选举是依赖Zookeeper来实现的,在Kafka集群中哪个broker能够成功创建/controller这个临时(EPHEMERAL)节点他就可以成为Kafka Controller。

这里需要说明一下的是Kafka Controller的实现还是相当复杂的,涉及到各个方面的内容,如果你掌握了Kafka Controller,你就掌握了Kafka的“半壁江山”。篇幅所限,这里就不一一展开了,有兴趣的读者可以查阅一下《深入理解Kafka》中第6章的相关内容。

分区leader的选举

这里不说什么一致性协议(PacificA)相关的内容,只讲述具体的选举内容。

分区leader副本的选举由Kafka Controller 负责具体实施。当创建分区(创建主题或增加分区都有创建分区的动作)或分区上线(比如分区中原先的leader副本下线,此时分区需要选举一个新的leader上线来对外提供服务)的时候都需要执行leader的选举动作。

基本思路是按照AR集合中副本的顺序查找第一个存活的副本,并且这个副本在ISR集合中。一个分区的AR集合在分配的时候就被指定,并且只要不发生重分配的情况,集合内部副本的顺序是保持不变的,而分区的ISR集合中副本的顺序可能会改变。注意这里是根据AR的顺序而不是ISR的顺序进行选举的。这个说起来比较抽象,有兴趣的读者可以手动关闭/开启某个集群中的broker来观察一下具体的变化。

还有一些情况也会发生分区leader的选举,比如当分区进行重分配(reassign)的时候也需要执行leader的选举动作。这个思路比较简单:从重分配的AR列表中找到第一个存活的副本,且这个副本在目前的ISR列表中。

再比如当发生优先副本(preferred replica partition leader election)的选举时,直接将优先副本设置为leader即可,AR集合中的第一个副本即为优先副本。

Kafka中有很多XX副本的称呼,如果不是很了解,可以关注本系列的下一篇《Kafka科普系列 | Kafka中到底有多少种副本?》

还有一种情况就是当某节点被优雅地关闭(也就是执行ControlledShutdown)时,位于这个节点上的leader副本都会下线,所以与此对应的分区需要执行leader的选举。这里的具体思路为:从AR列表中找到第一个存活的副本,且这个副本在目前的ISR列表中,与此同时还要确保这个副本不处于正在被关闭的节点上。

消费者相关的选举

对于这部分内容的理解,额。。如果你对消费者、消费组、消费者协调器以及组协调器不甚理解的话,那么。。。职能毛遂自荐《深入理解Kafka》一书了,嘿嘿。

组协调器GroupCoordinator需要为消费组内的消费者选举出一个消费组的leader,这个选举的算法也很简单,分两种情况分析。如果消费组内还没有leader,那么第一个加入消费组的消费者即为消费组的leader。如果某一时刻leader消费者由于某些原因退出了消费组,那么会重新选举一个新的leader,这个重新选举leader的过程又更“随意”了,相关代码如下:

//scala code.
private val members = new mutable.HashMap[String, MemberMetadata]
var leaderId = members.keys.head

解释一下这2行代码:在GroupCoordinator中消费者的信息是以HashMap的形式存储的,其中key为消费者的member_id,而value是消费者相关的元数据信息。leaderId表示leader消费者的member_id,它的取值为HashMap中的第一个键值对的key,这种选举的方式基本上和随机无异。总体上来说,消费组的leader选举过程是很随意的。

插播:近日发现文章被盗的厉害,发文几个小时文章就在各大门户网站上出现,全都是标的原创。虽然没有能力制止,但是我发现大多不仔细看直接抄的(有连我下面的公众号二维码也抄了去的,当然也有用PS来P掉我图片水印的鸡贼操作),所以机智的我。。不如在文章中插播一下我的书,让他们盗了去也好替我宣传宣传。我是皮的很~~

到这里就结束了吗?还有分区分配策略的选举呢。

许你对此有点陌生,但是用过Kafka的同学或许对partition.assignment.strategy(取值为RangeAssignor、RoundRobinAssignor、StickyAssignor等)这个参数并不陌生。每个消费者都可以设置自己的分区分配策略,对消费组而言需要从各个消费者呈报上来的各个分配策略中选举一个彼此都“信服”的策略来进行整体上的分区分配。这个分区分配的选举并非由leader消费者决定,而是根据消费组内的各个消费者投票来决定的。至于具体的细节么。。。嘿嘿。


欢迎支持笔者小册:《图解Kafka之实战指南》和《图解Kafka之核心原理


欢迎支持笔者新作:《深入理解Kafka:核心设计与实践原理》和《RabbitMQ实战指南》,同时欢迎关注笔者的微信公众号:朱小厮的博客。