Kafka在数据层面上的主从是Partition的主从,一个topic的一个partition的Replica(包括Referred Replica也即Leader Partition)分布在多个Broker上,一个Broker上应该只有特定partition的其中一个replica,因而用broker id 作为replica id。一般而言,Kafka的Leader partition应该在各个broker上均匀分布,以实现负载均衡,因为kafka中只有leader partiton对外提供读写服务,而其他replica仅仅是数据备份。
关于partition 的leader选举,最简单最直观的方案是:
抢注Leader节点——非公平模式
1. 创建Leader父节点,如 /chroot,并将其设置为persist节点
2. 各客户端通过在/chroot下创建Leader节点,如/chroot/leader,来竞争Leader。该节点应被设置为ephemeral
3. 若某创建Leader节点成功,则该客户端成功竞选为Leader
4. 若创建Leader节点失败,则竞选Leader失败,在/chroot/leader节点上注册exist的watcher,一旦该节点被删除则获得通知
5. Leader可通过删除Leader节点来放弃Leader
6. 如果Leader宕机,由于Leader节点被设置为ephemeral,Leader节点会自行删除。而其它节点由于在Leader节点上注册了watch,故可得到通知,参与下一轮竞选,从而保证总有客户端以Leader角色工作
先到先得,后者监视前者——公平模式
1. 创建Leader父节点,如 /chroot,并将其设置为persist节点
2. 各客户端通过在/chroot下创建Leader节点,如/chroot/leader,来竞争Leader。该节点应被设置为ephemeral_sequential
3.客户端通过getChildren方法获取/chroot/下所有子节点,如果其注册的节点的id在所有子节点中最小,则当前客户端竞选Leader成功
4.否则,在前面一个节点上注册watch,一旦前者被删除,则它得到通知,返回step 3(并不能直接认为自己成为新Leader,因为宕机的不一定是leader)
5.Leader节点可通过自行删除自己创建的节点以放弃Leader
然而该方法会有3个问题:
1.split-brain 这是由Zookeeper的特性引起的,虽然Zookeeper能保证所有Watch按顺序触发,但并不能保证同一时刻所有Replica“看”到的状态是一样的,这就可能造成不同Replica的响应不一致
2.herd effect 如果宕机的那个Broker上的Partition比较多,会造成多个Watch被触发,造成集群内大量的调整
3.Zookeeper负载过重 每个Replica都要为此在Zookeeper上注册一个Watch,当集群规模增加到几千个Partition时Zookeeper负载会过重。
Controller
Kafka 0.8.*的Leader Election方案解决了上述问题,它在所有broker中选出一个controller,所有Partition的Leader选举都由controller决定。controller会将Leader的改变直接通过RPC的方式(比Zookeeper Queue的方式更高效)通知需为此作出响应的Broker。同时controller也负责增删Topic以及Replica的重新分配。
Controller本身也要做failure over,它的故障转移依赖的就是Zookeeper了。broker实例启动时,如果当前尚无Leader,所有Broker都推举自己成为leader,准备好electionData 用前面那种方法在zk上创建Ephemeral Znode,写入数据,写入成功的那个broker即成为leader也即controller,其他就在结点上注册watcher。竞选为leader后调用若干回调方法,内存准备数据,设置各种监听,其中一个操作会increase zk上的controller epoch。
当有broker挂掉时,所有的broker注册在zk上的watch会被fire,broker判断当前是否存在leader,没有就竞选。
总结
kafka的主从有两种,一种是数据层面上的partition主从,一种是服务器broker层面上的主从。Partition主从依赖controller,controller的主从依赖zookeeper,zookeeper的主从依赖Zab机制以及Paxox算法。