Kafka如何避免重平衡

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

重平衡,也就是 Rebalance, 就是让一个 Consumer Group 下所有的 Consumer 实例就如何消费订阅主题的所有分区达成共识的过程。在 Rebalance 过程中,所有 Consumer 实例共同参与,在协调者组件的帮助下,完成订阅主题分区的分配。但是,在整个过程中,所有实例都不能消费任何消息,因此它对 ConsumerTPS 影响很大

重平衡的弊端

  1. Rebalance 影响 Consumer 端 TPS。在 Rebalance 期间,Consumer 会停下手头的事情,什么也不能做。
  2. Rebalance 很慢。如果你的 Group 下成员很多,需要停机很久
  3. Rebalance 效率不高。当前 Kafka 的设计机制决定了每次 Rebalance 时,Group 下的所有成员都要参与进来,而且通常不会考虑局部性原理,但局部性原理对提升系统性能是特别重要的。

遗憾的是,我没没有办法避免或者说解决重平衡问题,但是我们可以避免不必要的重平衡发生,减少重平衡操作。

如何避免重平衡

重平衡发生的时机

  1. 组成员数量发生变化
  2. 订阅主题数量发生变化
  3. 订阅主题的分区数发生变化

因为后面两个通常都是运维的主动操作,所以它们引发的 Rebalance 大都是不可避免的,(其实生产环境也很少发生后面这两个重平衡)。我们主要解决因为组成员数量变化而引发的 Rebalance 该如何避免。

组员数量变化发生重平衡

这里先穿插一下协调者的概念

所谓协调者,在 Kafka 中对应的术语是 Coordinator,它专门为 Consumer Group 服务,负责为 > Group 执行 Rebalance 以及提供位移管理和组成员管理等。
所有 Broker 在启动时,都会创建和开启相应的 Coordinator 组件。Consumer Group Kafka 内部位移主题 __consumer_offsets 确定为它服务的 Coordinator 在哪台 Broker 上。

Kafka 为某个 Consumer Group 确定 Coordinator 所在的 Broker 的算法有 2 个步骤。

  1. 确定由位移主题的哪个分区来保存该 Group 数据:partitionId=Math.abs(groupId.hashCode() % offsetsTopicPartitionCount)
  2. 找出该分区 Leader 副本所在的 Broker,该 Broker 即为对应的 Coordinator。
  1. 增加Consumer实例

当我们启动一个配置有相同group.id 值的 Consumer 程序时,实际上就向这个 Group 添加了一个新的 Consumer 实例。此时,Coordinator 会接纳这个新实例,将其加入到组中,并重新分配分区。通常来说,增加 Consumer 实例的操作都是计划内的,可能是出于增加 TPS 或提高伸缩性的需要。总之,它不属于我们要规避的那类“不必要 Rebalance”。

  1. 减少Consumer实例

停掉某些 Consumer 实例,那自不必说,关键是在某些情况下,Consumer 实例会被 Coordinator 错误地认为“已停止”从而被“踢出”Group。如果是这个原因导致的 Rebalance,这时,我们就需要采取措施避免重平衡

Consumer异常退组的情况

  1. 未能及时发送心跳,导致 Consumer 被“踢出”Group

    每个 Consumer 实例都会定期地向 Coordinator 发送心跳请求,表明它还存活着。如果某个 Consumer 实例不能及时地发送这些心跳请求,Coordinator 就会认为该 Consumer 已经“死”了,从而将其从 Group 中移除,然后开启新一轮 Rebalance。

    • Consumer 端有个参数,叫 session.timeout.ms,该参数的默认值是 10 秒,如果 Coordinator 在 10 秒之内没有收到 Group 下某 Consumer 实例的心跳,它就会认为这个 Consumer 实例已经挂了。
    • 除了这个参数,Consumer 还提供了一个允许你控制发送心跳请求频率的参数,就是 heartbeat.interval.ms。这个值设置得越小,Consumer 实例发送心跳请求的频率就越高。频繁地发送心跳请求会额外消耗带宽资源,但好处是能够更加快速地知晓当前是否开启 Rebalance,因为,目前 Coordinator 通知各个 Consumer 实例开启 Rebalance 的方法,就是将 REBALANCE_NEEDED 标志封装进心跳请求的响应体中。

我们可以设置

  • 设置 session.timeout.ms = 6s。
  • 设置 heartbeat.interval.ms = 2s。

保证 Consumer 实例在被判定为“dead”之前,能够发送至少 3 轮的心跳请求。

  1. Consumer 消费时间过长

    Consumer 端有一个参数,用于控制 Consumer 实际消费能力对 Rebalance 的影响,即 max.poll.interval.ms 参数。它限定了 Consumer 端应用程序两次调用 poll 方法的最大时间间隔。它的默认值是 5 分钟,表示你的 Consumer 程序如果在 5 分钟之内无法消费完 poll 方法返回的消息,那么 Consumer 会主动发起“离开组”的请求,Coordinator 也会开启新一轮 Rebalance。

如果我们的业务消费消息的时间确实很长,我们可以适当将这个参数调大,如果我们的业务消费时间不长,但是还是经过很久才提交位移,那么我们需要检查一下是否我们的程序频繁的发生了Full GC导致的,如果是,需要对JVM进行调优,有关JVM调优的方案可以参照 保姆级JVM调优实战

总结

  1. 重平衡会影响我们程序的吞吐量
  2. 排查重平衡发生的原因,避免Consumer端异常情况下的重平衡
  3. 通过设定Consumer端的参数,或者进行JVM调优避免不必要的重平衡
  • session.timeout.ms
  • heartbeat.interval.ms
  • max.poll.interval.ms
  • GC 参数
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享