kafka负载均衡

Kafka是一款高性能、高可用的分布式发布订阅消息系统,为了将资源最大化的利用,所以针对broker的负载均衡就尤为重要。以下整理了保障集群的负载相对均衡的一些机制。

微信图片_20211125213121.png

partition replica 负载均衡

我们知道生产/消费的真正实体是对应的分区的leader,所以服务端的负载是否均衡就基本上取决于leader分布的是否均匀。

那么服务端是如何尽可能的让leader分布均匀的呢?

首先我们需要了解在创建topic的时候分区副本是如何分布的,然后再了解是如何从这些副本中选出leader的。

我们先来看看topic创建时,分区副本的分布过程是什么样子的,我们假设创建一个TopicA,有5分区,3副本

up-4b3cfe573c187eac43b664f898edde7c9b7.png

过程概述:

  1. 随机挑选一个startIndex,图示中挑选的startIndex = 0

  2. tartIndex`开始按照 broker顺序 生成对应的第一个副本。

  3. 下一轮从startIndex + 1开始顺序生成第二个副本,以此类推。

这里的随机挑选也是保障服务端负载均衡的一部分,让分区尽可能的打散到各个broker中。

leader 负载均衡

此时,我们可以得到分区副本的分部列表

up-59522a3c92dec319889c398e837be2162df.png

那么如何从这些副本中选出leader的?基于优先副本,也就是挑选出AR列表中的第一个brokerId上的副本成为leader。

另外补充一下,随着时间的推移,集群状态的变化,或多或少会发生一些leader的切换和迁移,可能会导致某些broker上的leader会多一些从而导致负载不均衡,kafka针对这个问题提供了分区自动重平衡的功能,对应broker端的参数是 auto.leader.rebalance.enable,默认为true即开启。

开启之后呢,Controller会启动一个定时任务,每隔一段时间(5分钟)会去轮询所有的broker,计算每个broker节点的分区不平衡率,看是否超过了设置的阈值(默认10%)如果达到则自动进行分区迁移,将leader副本迁移回原先的优先副本所在的broker。

需要注意的是,这个自动迁移的过程可能会引起负面的性能问题,从而影响到现有的业务,所以需要根据业务情况来判断是否要开启这个功能;另外kafka有提供专门的脚本去手动执行该迁移操作:kafka-preferred-replica-election.sh

consumer group 负载均衡

通过 partition.assignment.strategy 参数去设置

RangeAssignor

按照 Topic 的维度进行分配的,也就是说按照Topic 对应的每个分区平均的按照范围区段分配给Consumer 实例。这种分配方案是按照Topic 的维度去分发分区的,此时可能会造成先分配分区的Consumer 实例的任务过重。

ebaee2a0b1988a232ce4b1d8f5bce567.jpg

RoundRobinAssignor

可以理解为为轮询,也就是顺序一个一个的分发。其中代码里面的大概逻辑如下:拿到组内所有Consumer 订阅的 Topic Partition,按照顺序挨个分发给 Consumer,此时如果和当前Consumer 没有订阅关系,则寻找下一个Consumer。从上面逻辑可以看出,如果组内每个消费者的订阅关系是同样的,这样TopicPartition 的分配是均匀的。

98af62320e6991bb59e84957ca8d8b79.jpg

当组内每个消费者订阅的Topic 是不同的,这样就可能会造成分区订阅的倾斜。

1da8109e8b0dc18a07977500b1194e63.jpg

StickyAssignor

Kafka从0.11 版本开始引入,是分配策略中最复杂的一种,从字面上可以看出是具有“粘性”的分区策略,其主要实现了两个功能:

  • 主题分区的分配要尽可能的均匀;
  • 当Rebalance 发生时,尽可能保持上一次的分配方案。

当然,当上面两个条件发生冲突是,第一个提交件要优先于第二个提交,这样可以使分配更加均匀。下面我们看一下官方提供的 2 个例子,来看一下RoundRoubin 和 Sticky 两者的区别。

34fa1007f5390297e829b1ce5a0f1e5a.jpg

从上面我们可以看出,初始状态各个Consumer 订阅是相同的时候,并且主题的分区数也是平均的时候,两种分配方案的结果是相同的。但是当Rebalance 发生时,可能就会不太相同了,加入上面的C1 发生了离组操作,此时分别会有下面的 Rebalance 结果:

7daff6f631272a2d49fad9350f159256.jpg

从上面Rebalance 后的结果可以看出,虽然两者最后分配都是均匀的,但是RoundRoubin 完全是重新分配了一遍,而Sticky 则是在原先的基础上达到了均匀的状态。

broker controller 选举

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

controller_epoch

  • controller_epoch记录控制器发生变更的次数,初始值为1,当控制器发生变更时,每选出一个新的控制器就将该字段值加1。

  • 每个和控制器交互的请求都会携带上controller_epoch这个字段,如果请求的controller_epoch值小于内存中的controller_epoch值,则认为这个请求是向已经过期的控制器所发送的请求,那么这个请求会被认定为无效的请求。

leader 选举

由controller执行:

  • 从Zookeeper中读取当前分区的所有ISR(in-sync replicas)集合
  • 调用配置的分区选择算法选择分区的leader

broker controller 作用

主题管理(创建、删除、增加分区)

  • 帮助我们完成对 Kafka 主题的创建、删除以及分区增加的操作。

分区重分配

  • 分区重分配主要是指,kafka-reassign-partitions 脚本,提供的对已有主题分区进行细粒度的分配功能。这部分功能也是控制器实现的。

Preferred 领导者选举

  • Preferred 领导者选举主要是 Kafka 为了避免部分 Broker 负载过重而提供的一种换 Leader 的方案

集群成员管理(新增 Broker、Broker 主动关闭、Broker 宕机)

  • 包括自动检测新增 Broker、Broker 主动关闭及被动宕机。这种自动检测是依赖于前面提到的 Watch 功能和 ZooKeeper 临时节点组合实现的。
  • 比如,控制器组件会利用 Watch 机制检查 ZooKeeper 的 /brokers/ids 节点下的子节点数量变更。目前,当有新 Broker 启动后,它会在 /brokers 下创建专属的 znode 节点。一旦创建完毕,ZooKeeper 会通过 Watch 机制将消息通知推送给控制器,这样,控制器就能自动地感知到这个变化,进而开启后续的新增 Broker 作业。
  • 侦测 Broker 存活性则是依赖于刚刚提到的另一个机制:临时节点。每个 Broker 启动后,会在 /brokers/ids 下创建一个临时 znode。当 Broker 宕机或主动关闭后,该 Broker 与 ZooKeeper 的会话结束,这个 znode 会被自动删除。同理,ZooKeeper 的 Watch 机制将这一变更推送给控制器,这样控制器就能知道有 Broker 关闭或宕机了,从而进行“善后”。

数据服务

就是向其他 Broker 提供数据服务。控制器上保存了最全的集群元数据信息,其他所有 Broker 会定期接收控制器发来的元数据更新请求,从而更新其内存中的缓存数据。

  • 所有主题信息。包括具体的分区信息,比如领导者副本是谁,ISR 集合中有哪些副本等。

  • 所有 Broker 信息。包括当前都有哪些运行中的 Broker,哪些正在关闭中的 Broker 等。

zookeeper 作用

Kafka主要使用ZooKeeper来保存它的元数据、监控Broker和分区的存活状态,并利用ZooKeeper来进行选举。

  • 保存Kafka的Broker信息/brokers/ids/[0...N]
  • 保存主题和分区的信息/brokers/topics/
  • 保存分区当前的leader和所有的ISR的BrokerID

猜你喜欢

转载自juejin.im/post/7040331110831620110