Kafka协调器和有意思的三个参数

一、Kafka的协调器

  • 作用: 负责消费者的出入组工作
  • 组协调器,每个broker启动的时候,都会创建GroupCoordinator实例,管理部分消费组
    • 在与之连接的消费者中选举出消费者leader
    • 下发leader消费者返回的消费者分区分配结果给所有的消费者
    • 管理消费者的消费偏移量提交,保存在kafka的内部主题中
    • 和消费者心跳保持,知道哪些消费者已经死掉,组中存活的消费者是哪些
  • 消费者协调器,每个consumer实例化时,同时实例化一个ConsumerCoordinator对象,负责同一个消费组下各个消费者和服务端组协调器之前的通信
    • 向组协调器申请加入组或者离开组
    • 向组协调器提交偏移量
    • 通过心跳,保持组协调器的连接感知
    • 被组协调器选为leader的消费者的协调器,负责消费者分区分配。分配结果发送给组协调器
    • 非leader的消费者,通过消费者协调器和组协调器同步分配结果
选举GroupCoordinator流程:
  1. 首先对消费组的groupId进行hash,接着对consumer_offsets的分区数量取模,默认是50,可以通过offsets.topic.num.partitions来设置
  2. 找到你的这个consumer group的offset要提交到consumer_offsets的哪个分区
  3. 然后对这个分区找到对应的leader所在的broker,这个broker就是这个consumer group的coordinator了,consumer接着就会维护一个Socket连接跟这个Broker进行通信
选举消费者leader过程
  1. 确定了GroupCoordinator之后,所有的consumer都会发送一个join group请求注册
  2. GroupCoordinator就会默认把第一个注册上来的consumer选择成为leader consumer
  3. 把整个Topic的情况(Kafka元数据)下发给leader consumer
  4. leader consumer就会根据负载均衡的思路制定消费方案,返回给GroupCoordinator
  5. GroupCoordinator拿到方案之后再下发给所有的consumer
  6. consumer都会向GroupCoordinator发送心跳,当有consumer长时间不再和GroupCoordinator保持联系,就会重新把分配给这个consumer的任务重新执行一遍

二、有意思的参数

1. heartbeat.interval.ms参数

每个consumer 都会根据 heartbeat.interval.ms 参数指定的时间周期性地向group coordinator发送 hearbeat,group coordinator会给各个consumer响应,若发生了 rebalance,各个consumer收到的响应中会包含 REBALANCE_IN_PROGRESS 标识,这样各个consumer就知道已经发生了rebalance,同时 group coordinator也知道了各个consumer的存活情况

2. session.timeout.ms参数

group coordinator检测consumer发生崩溃所需的时间。一个consumer group里面的某个consumer挂掉了,最长需要 session.timeout.ms 秒检测出来

3. max.poll.interval.ms参数

如果在两次poll操作之间,超过了这个时间,会进行重平衡

参数使用举例

session.timeout.ms=10,
heartbeat.interval.ms=3

session.timeout.ms是个"逻辑"指标,它指定了一个阈值—10秒,在这个阈值内如果coordinator未收到consumer的任何消息,那coordinator就认为consumer挂了。而heartbeat.interval.ms是个"物理"指标,它告诉consumer要每3秒给coordinator发一个心跳包,heartbeat.interval.ms越小,发的心跳包越多

设计的原因: 如果group coordinator在一个heartbeat.interval.ms周期内未收到consumer的心跳,就把该consumer移出group,这样设计显得不合理,有可能网络延时,有可能consumer出现了一次长时间GC,影响了心跳包的到达,就会造成误判,导致频繁的rebalance

版本对比

  • 在kafka0.10.1之前,发送心跳包和消息处理逻辑这2个过程是耦合在一起的,如果一条消息处理时长要5min,而session.timeout.ms=3000ms,那么等 kafka consumer处理完消息,group coordinator早就将consumer 移出group了,,因为只有一个线程,在消息处理过程中就无法向group coordinator发送心跳包,超过3000ms未发送心跳包,group coordinator就将该consumer移出group了

  • kafka0.10.1之后的版本中,new KafkaConsumer对象后,在while true循环中执行consumer.poll拉取消息这个过程中,存在两个线程:

    • heartbeat 线程,定时发送心跳
    • processing线程,可理解为调用consumer.poll方法执行消息处理逻辑的线程
  • 两个线程设计的优点

    • 将二者分开,一个processing线程负责执行消息处理逻辑,一个heartbeat线程负责发送心跳包,那么:就算一条消息需要处理5min,只要底heartbeat线程在session.timeout.ms向group coordinator发送了心跳包,那consumer可以继续处理消息,而不用担心被移出group
    • 如果consumer出了问题,那么在 session.timeout.ms内就能检测出来,而不用等到 max.poll.interval.ms 时长后才能检测出来

实际问题分析

项目中经常碰到的 频繁consumer rebalance 错误

  • 分析: 虽然是两个线程,可能消息处理线程执行时间很长,但是心跳线程一直在发送信息,看起来应该不会发生重平衡,为什么group coordinator怎么还老是将consumer移出group,然后导致不断地rebalance呢?

  • 主要是由max.poll.interval.ms这个参数引起的,消息处理逻辑花了太长的时间,超过了max.poll.interval.ms ,那么此consumer提交offset就会失败。此外,在用户线程中,一般会做一些失败的重试处理,比如通过线程池的 ThreadPoolExecutor#afterExecute()方法捕获到异常,再次提交Runnable任务重新订阅kafka topic。那么意味着有新消费者加入group,就会引发 rebalance,而可悲的是:新的消费者还是来不及处理完所有消息,又被移出group。如此循环,就发生了不停地 rebalance 的现象

避免重平衡参数设置

  • 前提:重平衡无法避免。只能通过合理配置减少

  • session.timeout.ms 和 heartbeat.interval.ms参数设置

    • 设置 session.timeout.ms = 10s。
    • 设置 heartbeat.interval.ms = 3s。
    • 要保证 Consumer 实例在被判定为“dead”之前,能够发送至少 3 轮的心跳请求,即 session.timeout.ms >= 3 * heartbeat.interval.ms
  • max.poll.interval.ms 参数值的设置 ,默认是5分钟。可以根据业务的实际处理实际,比如业务处理时间是8分钟,可以将其调成10分钟,避免频繁的rebalance

  • 调整JVM参数,避免频繁GC导致的重平衡(JVM高手根据实际情况合理设置)

参考

Kafka session.timeout.ms heartbeat.interval.ms参数的区别以及对数据存储的一些思考

你可能感兴趣的:(Kafka)