1. Rebalance 触发与通知

1.1. 触发条件

Rebalance 的触发条件有三种:

  • 当 Consumer Group 组成员数量发生变化
  • 新成员加入
  • 组成员主动离开
  • 组成员崩溃
  • 消费者心跳超时,导致 rebalance
  • 消费者处理时间过长,导致 rebalance。
  • 当订阅主题数量发生变化
  • 当订阅主题的分区数发生变化

组成员崩溃外,其它都是主动触发的,能比较好地控制。
组成员崩溃 则是预料不到、意外发生的,遇到问题的时候也不好排查。但对于组成员崩溃也是有一些通用的处理策略

1.2. 通知其他 consumer 进程

Rebalance 如何通知其他 consumer 进程?
Rebalance 的通知机制是靠 Consumer 端的心跳线程

  • Consumer 端会定期发送心跳请求到 Broker 端的 Coordinator,
  • 当协调者决定开启 Rebalance 后,它会将“REBALANCE_IN_PROGRESS”封装进心跳请求的响应中发送给 Consumer ,
  • 当 Consumer 发现心跳响应中包含了“REBALANCE_IN_PROGRESS”,就知道 Rebalance 开始了。

1.3. Rebalance相关的概念

rebalance(重平衡)其实就是重新进行 partition 的分配,从而使得 partition 的分配重新达到平衡状态

1.3.1. Coordinator介绍

Coordinator一般指的是运行在broker上的group Coordinator,用于管理Consumer Group中各个成员,每个KafkaServer都有一个GroupCoordinator实例,管理多个消费者组,主要用于offset位移管理和Consumer Rebalance。

Coordinator存储的信息

对于每个Consumer Group,Coordinator会存储以下信息:

  • 对每个topic,可能有多个消费组group订阅同一个topic
  • 对每个Consumer Group,元数据如下:
  • 订阅的topics列表
  • Consumer Group配置信息,包括session timeout等
  • 组中每个Consumer的元数据。包括主机名,consumer id
  • 每个正在消费的topic partition的当前offsets
  • Partition的ownership元数据,包括consumer与partitions映射关系
如何确定consumer group的coordinator

简单来说分为两步:

  • 确定consumer group位移信息写入__consumers_offsets这个topic的哪个分区。具体计算公式:
    __consumers_offsets partition# = Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount) 注意:groupMetadataTopicPartitionCount由offsets.topic.num.partitions指定,默认是50个分区。
  • 该分区leader所在的broker就是被选定的coordinator

1.3.2. Rebalance 协议 (protocol) 说明

Rebalance 本质上也是一组协议。Consumer Group 与 Coordinator 共同使用它来完成 Consumer Group 的 Rebalance

  • Heartbeat请求:Consumer 需要定期给 Coordinator 发送心跳来证明自己还活着。
  • LeaveGroup请求:主动告诉 Coordinator 要离开 Consumer Group
  • SyncGroup请求:Group Leader Consumer 把分配方案告诉组内所有成员
  • JoinGroup请求:成员请求加入组
  • DescribeGroup请求:显示组的所有信息,包括成员信息,协议名称,分配方案,订阅信息等。通常该请求是给管理员使用。

Coordinator 在 Rebalance 的时候主要用到了前面4种请求

1.3.3. Consumer Group 状态机

Rebalance 一旦发生,必定会涉及到 Consumer Group 的状态流转,此时 Kafka 为我们设计了一套完整的状态机机制,来帮助 Broker Coordinator 完成整个重平衡流程

  • Empty 状态表示当前组内无成员, 但是可能存在 Consumer Group 已提交的位移数据,且未过期,这种状态只能响应 JoinGroup 请求。
  • Dead 状态表示组内已经没有任何成员的状态,组内的元数据已经被 Broker Coordinator 移除,这种状态响应各种请求都是一个Response:UNKNOWN_MEMBER_ID。
  • PreparingRebalance 状态表示准备开始新的 Rebalance, 等待组内所有成员重新加入组内。
  • CompletingRebalance 状态表示组内成员都已经加入成功,正在等待分配方案,旧版本中叫“AwaitingSync”。
  • Stable 状态表示 Rebalance 已经完成, 组内 Consumer 可以开始消费了。

    通过上面5种状态可以看出,Rebalance 主要分为两个步骤:加入组(对应JoinGroup请求)和等待 Leader Consumer 分配方案(SyncGroup 请求)。

2. rebalance问题处理策略

2.1. 涉及的重要参数

  • session.timeout.ms: consumer 向 broker 发送心跳的超时时间。例如 session.timeout.ms = 180000 表示在最长 180 秒内 broker 没收到 consumer 的心跳,那么 broker 就认为该 consumer 死亡了,会引发 rebalance。
  • heartbeat.interval.ms: consumer 每次向 broker 发送心跳的时间间隔。heartbeat.interval.ms = 60000 表示 consumer 每 60 秒向 broker 发送一次心跳。一般来说,session.timeout.ms 的值是 heartbeat.interval.ms 值的 3 倍以上。
  • max.poll.interval.ms : consumer 每两次 poll 消息的时间间隔。简单地说就是 consumer 每次消费消息的时长。如果消息处理的逻辑很重,那么此值就要相应延长。否则如果时间到了但 consumer 还么消费完,broker 会默认认为 consumer 死了,发起 rebalance。
  • max.poll.records: 每次消费的时候,获取多少条消息。获取的消息条数越多,需要处理的时间越长。所以每次拉取的消息数不能太多,需要保证在 max.poll.interval.ms 设置的时间内能消费完,否则会引发 rebalance。

所以消费者心跳超时、消费者处理时间过长都会引起rebalance

2.2. 问题处理策略

2.2.1. 消费者心跳超时

消费者是通过心跳和协调者保持通讯的,如果协调者收不到心跳,那么协调者会认为这个消费者死亡了,从而发起 rebalance。

在kafka 的消费者参数设置中,与心跳相关的两个参数为:

  • session.timeout.ms 设置了超时时间
  • heartbeat.interval.ms 心跳时间间隔

需要调整 session.timeout.ms 和 heartbeat.interval.ms 参数,使得消费者与协调者能保持心跳。
一般来说,超时时间应该是心跳间隔的 3 倍时间,因为这样的话,在一个超时周期内就可以有多次心跳,避免网络问题导致偶发失败。
session.timeout.ms 如果设置为 180 秒,那么 heartbeat.interval.ms 最多设置为 60 秒。

2.2.2. 消费者处理时间过长

如果消费者处理时间过长,那么同样会导致协调者认为该 consumer 死亡了,从而发起rebalance。

在 kafka 的消费者参数设置中,与消费处理的两个参数为:

  • max.poll.interval.ms 每次消费的处理时间
  • max.poll.records 每次消费的消息数

对于这种情况,一般来说就是增加消费者处理的时间(即提高 max.poll.interval.ms 的值),减少每次处理的消息数(即减少 max.poll.records 的值)。

除此之外,超时时间参数(session.timeout.ms)与 消费者每次处理的时间(max.poll.interval.ms)也是有关联的。max.poll.interval.ms 时间不能超过 session.timeout.ms 时间。 因为在 kafka 消费者的实现中,是单线程去消费消息和执行心跳的,如果线程卡在处理消息,那么这时候即使到时间要心跳了,还是没有线程可以去执行心跳操作。

对于 rebalance 类问题的处理策略,简单来讲就是处理好心跳超时问题和消费处理超时问题

对于心跳超时问题。一般是调整超时时间(session.timeout.ms)和心跳间隔时间(heartbeat.interval.ms)的比例及数值。

阿里云官方文档建议超时时间(session.timeout.ms)设置成 25s,最长不超过 30s。那么心跳间隔时间(heartbeat.interval.ms)就不超过 10s。
对于消费处理超时问题。一般是增加消费者处理的时间(max.poll.interval.ms),减少每次处理的消息数(max.poll.records)。
阿里云官方文档建议 max.poll.records 参数要远小于当前消费组的消费能力(records < 单个线程每秒消费的条数 * 消费线程的个数 * session.timeout的秒数)。