kafka的选举和负载均衡策略

kafka的选举和负载均衡策略

1、kafka Controller leader:主broker的选举

主broker的作用:Controller Broker的主要职责有很多,主要是一些管理行为,主要包括以下几个方面:

  • 创建、删除主题,增加分区并分配leader分区
  • 集群Broker管理(新增 Broker、Broker 主动关闭、Broker 故障)
  • preferred leader选举
  • 分区重分配

主broker的选举流程:Kafka的Leader选举是通过在zookeeper上创建/controller临时节点来实现leader选举,并在该节点中写入当前broker的信息 {“version”:1,”brokerid”:1,”timestamp”:”1512018424988”} 。利用Zookeeper的强一致性特性,一个节点只能被一个客户端创建成功,创建成功的broker即为leader,即先到先得原则,leader也就是集群中的controller,负责集群中所有大小事务。 当leader和zookeeper失去连接时,临时节点会删除,而其他broker会监听该节点的变化,当节点删除时,其他broker会收到事件通知,重新发起leader选举。

每个Broker都会在Controller Path (/controller)上注册一个Watch。 当前Controller失败时,对应的Controller Path会自动消失(因为它是ephemeral Node),此时该Watch被fire,所有“活” 着的Broker都会去竞选成为新的Controller (创建新的Controller Path),但是只会有一个竞选成功(这点由Zookeeper保证)。竞选成功者即为新的Leader,竞选失败者则重新在新的Controller Path上注册Watch。因为Zookeeper的Watch是一次性的, 被fire一次之后即失效,所以需要重新注册。
在这里插入图片描述

2、kafka Partition leader :主分区的选举
  • 创建topic时:为了更好的做负载均衡,Kafka尽量将所有的Partition均匀分配到整个集群上。Kafka分配Replica的算法如下:

    副本因子不能大于 Broker 的个数;
    第一个分区(编号为0)的第一个副本放置位置是随机从 brokerList 选择的;
    其他分区的第一个副本放置位置相对于第0个分区依次往后移。也就是如果我们有5个 Broker,5个分区,假设第一个分区放在第四个 Broker 上,那么第二个分区将会放在第五个 Broker 上;第三个分区将会放在第一个 Broker 上;第四个分区将会放在第二个 Broker 上,依次类推;
    剩余的副本相对于第一个副本放置位置其实是由 nextReplicaShift 决定的,而这个数也是随机产生的

  • 创建topic后:

    • kafka在所有broker中选出一个controller 主broker,所有Partition的Leader选举都由controller决定。Zookeeper会针对每个Topic维护一个ISR(已同步的副本)集合,显然存在一些没有来得及完全同步数据的副本。只有在这个ISR列表中才有资格成为Leader(先试用ISR集合中的第一个,如果不行则依次类推,因为ISR里面是同步的副本,消息是最完全的且各个节点都是一样的)。
    • 如何处理所有Replica都不工作?:在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某个Partition的所有Replica都宕机了,就无法保证数据不丢失了。这种情况下有两种可行的方案:
      1. 等待ISR中的任一个Replica“活”过来,并且选它作为Leader
      2. 选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader
3、kafka 消费者选主

kafka引入了协调器。服务端引入组协调器(GroupCoordinator),消费者端引入消费者协调器(ConsumerCoordinator)。每个broker启动的时候,都会创建GroupCoordinator实例,管理部分消费组(集群负载均衡)和组下每个消费者消费的偏移量(offset)。Group Coordinator是一个服务,每个Broker在启动的时候都会启动一个该服务。Group Coordinator的作用是用来存储Group的相关Meta信息,并将对应Partition的Offset信息记录到Kafka内置Topic(__consumer_offsets)中。每个consumer实例化时,同时实例化一个ConsumerCoordinator对象,负责同一个消费组下各个消费者和服务端组协调器之前的通信。

组协调器负责处理消费者协调器发过来的各种请求。它主要提供如下功能:

  1. 在与之连接的消费者中选举出消费者leader
  2. 下发leader消费者返回的消费者分区分配结果给所有的消费者
  3. 管理消费者的消费偏移量提交,保存在kafka的内部主题中
  4. 和消费者心跳保持,知道哪些消费者已经死掉,组中存活的消费者是哪些。

consumer group是kafka提供的可扩展且具有容错性的消费者机制。既然是一个组,那么组内必然可以有多个消费者或消费者实例(consumer instance),它们共享一个公共的ID,即group ID。组内的所有消费者协调在一起来消费订阅主题(subscribed topics)的所有分区(partition)。当然,每个分区只能由同一个消费组内的一个consumer来消费。

一旦所有成员都发送了JoinGroup请求,组coordinator会从中选择一个consumer担任消费者leader的角色,并把组成员信息以及订阅信息发给leader——注意leader和coordinator不是一个概念。消费者leader负责消费分配方案的制定。

在kafka的消费端,会有一个消费者协调器以及消费组,组协调器GroupCoordinator须要为消费组内的消费者选举出一个消费组的leader,那么如何选举的呢?若是消费组内尚未leader,那么第一个加入消费组的消费者即为消费组的leader,若是某一个时刻leader消费者因为某些缘由退出了消费组,那么就会从新选举leader,如何选举?在GroupCoordinator中消费者的信息是以HashMap的形式存储的,其中key为消费者的member_id,而value是消费者相关的元数据信息。leaderId表示leader消费者的member_id,它的取值为HashMap中的第一个键值对的key,这种选举的方式基本上和随机无异。

private val members = new mutable.HashMap[String, MemberMetadata]
leaderId = members.keys.headOption

消费者leader负责分配消费方案,即哪个consumer负责消费哪些topic的哪些partition。一旦完成分配,leader会将这个方案封装进SyncGroup请求中发给coordinator。

  • RangeAssignor 以主题为单位,以数据顺序排列可用分区,以字典顺序排列消费者,将topic分区数除以消费者总数,以确定分配给每个消费者的分区数;如果没有平均分配,那么前几个消费者将拥有一个额外的分区。

    假如有10个分区,3个消费者线程,把分区按照序号排列0,1,2,3,4,5,6,7,8,9;消费者线程为C1-0,C2-0,C2-1,那么用partition数除以消费者线程的总数来决定每个消费者线程消费几个partition,如果除不尽,前面几个消费者将会多消费一个分区。在我们的例子里面,我们有10个分区,3个消费者线程,10/3 = 3,而且除除不尽,那么消费者线程C1-0将会多消费一个分区,所以最后分区分配的结果看起来是这样的:
    
    C1-0:0,1,2,3
    C2-0:4,5,6
    C2-1:7,8,9
    
  • RoundRobinAssignor 是kafka默认策略,对所有分区和所有消费者循环分配,分区更均衡。将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询方式逐个将分区以此分配给每个消费者。

4、kafka 生产者写入时的负载均衡策略

Kafka Producer产生数据发送给Kafka Server,具体的分发逻辑及负载均衡逻辑,全部由Producer维护。分为下面两种情况:

1、没有key的分发逻辑: 每隔 topic.metadata.refresh.interval.ms 的时间,随机选择一个partition。这个时间窗口内的所有记录发送到这个partition。发送数据后即便失败也会重新选择一个partition

2、根据key的分发逻辑:对key求hash,然后对partition数量求模

分区器是生产者层面的负载均衡。Kafka 生产者生产消息时,根据分区器将消息投递到指定的分区中,所以 Kafka 的负载均衡很大程度上依赖于分区器。Kafka 默认的分区器是 Kafka 提供的 DefaultPartitioner。它的分区策略是根据 Key 值进行分区分配的:

  • 如果 key 不为 null:对 Key 值进行 Hash 计算,从所有分区中根据 Key 的 Hash 值计算出一个分区号;拥有相同 Key 值的消息被写入同一个分区;
  • 如果 key 为 null:消息将以轮询的方式,在所有可用分区中分别写入消息。

如果不想使用 Kafka 默认的分区器,用户可以实现 Partitioner 接口,自行实现分区方法。

5、kafka分区的变动处理

对 Kafka 增加 Kafka 的分区数据,但是 Kafka 不支持减少分区数。 Kafka 分区数据不支持减少是由很多原因的,比如减少的分区其数据放到哪里去?是删除,还是保留?删除的话,那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面?追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性,那么实现起来逻辑就会非常复杂。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值