kafka 分区 leader 选举机制原理

本文详细介绍了Kafka中Broker故障时的Leader选举机制,包括Controller如何监控Broker状态、选举新Leader的过程以及LeaderAndISRRequest的处理流程。同时探讨了所有Replica不可用时的处理策略。

afka在所有broker中选出一个controller,所有Partition的Leader选举都由controller决定。controller会将Leader的改变直接通过RPC的方式(比Zookeeper Queue的方式更高效)通知需为此作出响应的Broker。同时controller也负责增删Topic以及Replica的重新分配。

当有broker fari over controller的处理过程如下:

1.Controller在Zookeeper注册Watch,一旦有Broker宕机(这是用宕机代表任何让系统认为其die的情景,包括但不限于机器断电,网络不可用,GC导致的Stop The World,进程crash等),其在Zookeeper对应的znode会自动被删除,Zookeeper会fire Controller注册的watch,Controller读取最新的幸存的Broker

2.Controller决定set_p,该集合包含了宕机的所有Broker上的所有Partition

3.对set_p中的每一个Partition

3.1 从/brokers/topics/[topic]/partitions/[partition]/state读取该Partition当前的ISR

3.2 决定该Partition的新Leader。如果当前ISR中有至少一个Replica还幸存,则选择其中一个作为新Leader,新的ISR则包含当前ISR中所有幸存的Replica(选举算法的实现类似于微软的PacificA)。否则选择该Partition中任意一个幸存的Replica作为新的Leader以及ISR(该场景下可能会有潜在的数据丢失)。如果该Partition的所有Replica都宕机了,则将新的Leader设置为-1。

3.3 将新的Leader,ISR和新的leader_epoch及controller_epoch写入/brokers/topics/[topic]/partitions/[partition]/state。注意,该操作只有其version在3.1至3.3的过程中无变化时才会执行,否则跳转到3.1

4. 直接通过RPC向set_p相关的Broker发送LeaderAndISRRequest命令。Controller可以在一个RPC操作中发送多个命令从而提高效率。

 

LeaderAndIsrRequest响应过程

 

1.若请求中controllerEpoch小于当前最新的controllerEpoch,则直接返回ErrorMapping.StaleControllerEpochCode。2.对于请求中partitionStateInfos中的每一个元素,即((topic, partitionId), partitionStateInfo):

2.1 若partitionStateInfo中的leader epoch大于当前ReplicManager中存储的(topic, partitionId)对应的partition的leader epoch,则:

2.1.1 若当前brokerid(或者说replica id)在partitionStateInfo中,则将该partition及partitionStateInfo存入一个名为partitionState的HashMap中

2.1.2否则说明该Broker不在该Partition分配的Replica list中,将该信息记录于log中2.2否则将相应的Error code(ErrorMapping.StaleLeaderEpochCode)存入Response中

3.筛选出partitionState中Leader与当前Broker ID相等的所有记录存入partitionsTobeLeader中,其它记录存入partitionsToBeFollower中。

4.若partitionsTobeLeader不为空,则对其执行makeLeaders方。

5.若partitionsToBeFollower不为空,则对其执行makeFollowers方法

6.若highwatermak线程还未启动,则将其启动,并将hwThreadInitialized设为true。

7.关闭所有Idle状态的Fetcher。

LeaderAndIsrRequest处理过程如下图所示

  对于收到的LeaderAndIsrRequest,Broker主要通过ReplicaManager的becomeLeaderOrFollower处理,流程如下:

 

 

如何处理所有Replica都不工作

 

  上文提到,在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某个Partition的所有Replica都宕机了,就无法保证数据不丢失了。这种情况下有两种可行的方案:

1.等待ISR中的任一个Replica“活”过来,并且选它作为Leader

2.选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader

  这就需要在可用性和一致性当中作出一个简单的折衷。如果一定要等待ISR中的Replica“活”过来,那不可用的时间就可能会相对较长。而且如果ISR中的所有Replica都无法“活”过来了,或者数据都丢失了,这个Partition将永远不可用。选择第一个“活”过来的Replica作为Leader,而这个Replica不是ISR中的Replica,那即使它并不保证已经包含了所有已commit的消息,它也会成为Leader而作为consumer的数据源(前文有说明,所有读写都由Leader完成)。Kafka0.8.*使用了第二种方式。根据Kafka的文档,在以后的版本中,Kafka支持用户通过配置选择这两种方式中的一种,从而根据不同的使用场景选择高可用性还是强一致性。 unclean.leader.election.enable 参数决定使用哪种方案,默认是true,采用第二种方案 

参考:

1. http://www.jasongj.com/2015/04/24/KafkaColumn2/

2. http://www.jasongj.com/2015/06/08/KafkaColumn3/

3. http://orchome.com/22

### Kafka 分区再平衡机制的工作原理 Kafka分区再平衡机制是消费者组协调器(Group Coordinator)和消费者客户端协作完成的,其核心目标是在消费者组成员发生变化时,重新分配分区以实现负载均衡和高可用性 [^1]。再平衡机制主要分为以下几个阶段: #### 1. 触发再平衡的条件 再平衡通常在以下几种情况下被触发: - 消费者组中新增或移除消费者实例; - 消费者实例发生故障或主动退出; - 主题的分区数量发生变化; - 消费者订阅的主题发生变化。 这些事件会促使消费者组进入“重新加入”(Rebalance)状态,从而触发分区重新分配 [^3]。 #### 2. 再平衡的流程 再平衡流程主要包括以下几个步骤: - **组协调器选举**:每个消费者组都有一个协调者(Coordinator),负责管理再平衡流程。协调者通常由 Kafka 集群中的一个 Broker 担任。 - **消费者加入组**:所有消费者实例向协调者发送 `JoinGroup` 请求,表明自己希望加入消费者组并参与再平衡。 - **分区分配策略协商**:消费者组中的一个“领导者消费者”(Leader Consumer)负责根据配置的分区分配策略(如 Range、Round-Robin、Sticky 等)进行分区分配。 - **分配结果同步**:领导者消费者将分配结果发送给协调者,协调者再将分配结果同步给所有消费者。 - **开始消费**:消费者根据新的分区分配结果开始消费数据 [^3]。 在整个再平衡过程中,消费者无法从 Kafka 集群中拉取消息,因此这段时间会影响系统的吞吐量和可用性 [^2]。 #### 3. 分区分配策略 Kafka 支持多种分区分配策略,常见的包括: - **Range 分配**:按分区和消费者的顺序进行范围划分,适用于分区数与消费者数比例较均衡的情况。 - **Round-Robin**:将分区和消费者视为独立集合,进行轮询分配,适用于更均匀的负载需求。 - **Sticky 分配**:在再平衡时尽量保持已有分区分配不变,减少消费中断,适用于对稳定性要求较高的场景 [^3]。 每种策略在再平衡时的性能和影响不同,应根据业务需求进行选择。 #### 4. 再平衡的影响与优化 再平衡期间,消费者无法正常消费数据,导致系统吞吐量下降,甚至可能出现服务不可用的情况。如果 Kafka 集群节点较多,再平衡过程可能持续数分钟到数小时 [^2]。 为降低再平衡的频率和影响,可以采取以下措施: - 增加会话超时时间(`session.timeout.ms`)和心跳间隔(`heartbeat.interval.ms`),避免因短暂网络波动导致消费者被误判为离线; - 合理配置消费者数量,避免频繁扩容或缩容; - 使用 Sticky 分配策略,减少分区重分配带来的影响; - 在消费者代码中实现优雅关闭,避免非正常退出触发再平衡 [^4]。 --- ### 示例代码:配置 Sticky 分区分配策略 ```java Properties props = new Properties(); props.put("bootstrap.servers", "localhost:9092"); props.put("group.id", "test-group"); props.put("enable.auto.commit", "false"); props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); props.put("partition.assignment.strategy", "org.apache.kafka.clients.consumer.StickyAssignor"); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("my-topic")); ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值