什么是 Rebalance?

Rebalance 为什么会发生?

Rebalance 的过程

记得之前在一段时间密集面试的时候总会问候选人这些问题。

什么是 Rebalance

重平衡 Rebalance 就是让整个 Consumer Group 下的所有的 Consumer 实例久如何消费订阅主题的所有分区达成共识的过程。在 Rebalance 的过程中,所有 Consumer 实例都需要参与进来,在 Coordinator 的帮助下完成分配。所以可以很明显的回答上面的第三个问题,在 Rebalance 的时候是无法进行消费的持续消费的。就可能会造成队列的段时间阻塞。

Rebalance 为什么会发生

1. 我们发现线上处理能力不够了,需要向 group 中增加新的 consumer ,就会触发 Rebalance 。任何新成员的「进入」和「离开」都会触发 Rebalance。消费会停下来重新给所有  Consumer 分配对应的 partiitons.

2.我们为 topic 增加分区,比如原本一个 topic 只有 10个分区,后因为性能问题需要扩展增加到 20 个分区就会发生 Rebanlace.

大的情况可以分为这两种,其实第一种新成员 「进入」 和 「离开」还可以细讲一下,因为 90% 以上的离开和进入都不是我们想要的结果。他有可能是消费超过超时时间被一脚踢出了 group 造成离开从而造成 Rebalance 。然后又因为踢出之后又去请求又意外的加入 group 从而继续引发 Rebalance 往复循环。

我们的消费者在与 broker 进行沟通的时候都是与一个叫 Coordinator 的组件进行交互, Coodinator 是专门负责管理消费者组 加入离开和位移的组件。

那么什么情况下 Coordinator 会认为某个 Consumer 实例挂了需要退组呢?

当 Rebalance 完成之后,我们的每个 Consumer 都会向 Coordinator 定时发送心跳,以表明客户端还活着。

如果某个 Consumer 没有按照约定好的规则发送请求给 Coordinator ,Coordinator就会认为这个 Consumer 已经挂了,并将其一脚踢出 Group 然后通过心跳包 response 组内其他 Consumer 尽快开始 Rebalance。这里有一点需要注意,Consumer 心跳参数 heartbeat_interval_ms 会在一个 session_timeout 周期内决定是否 Consumer 已经离开。

打个比方,如果我们的 session_timeout_ms 设置为默认的 10s 那么如果心跳三次 heartbeat_interval_ms 都失败那么就会让 Coordinator 开启重平衡。而 Coordinator 通知组内其他消费者的办法也是通过消费者发送的心跳,在 broker 对他进行 response 的时候塞进 REBALANCE_NEEDED 标志位,通知他们进行 rebalance。

Kafka broker 端有个日志叫 server.log ,在这个日志中我们可以看到 GroupCoordinator 的身影

[-- ::,] INFO [GroupCoordinator ]: Preparing to rebalance group answer_action with old generation  (__consumer_offsets-) (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Group answer_action with generation is now empty (__consumer_offsets-) (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Preparing to rebalance group answer_action with old generation (__consumer_offsets-) (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Stabilized group answer_action generation (__consumer_offsets-) (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Assignment received from leader for group answer_action for generation (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Member kafka-python-1.3.-3feb5deb-de82-40a4-8da9-1c6ec7d1ca4f in group answer_action has failed, removing it from the group (kafka.coordinator.group.GroupCoordinator)

从第一条开始可以看到是 preparing to rebalance ,group-id 是 answer_action

但是大家注意

[-- ::,] INFO [GroupCoordinator ]: Member kafka-python-1.3.-3feb5deb-de82-40a4-8da9-1c6ec7d1ca4f in group answer_action has failed, removing it from the group (kafka.coordinator.group.GroupCoordinator)

因为 group 中其中有一个 consumer 的异常,被 coordinator 踢出了 group 。

所以平时 Kafka 出现的 Rebalance 相关的异常我们可以很容易的在日志中发现,并且可以得到一些细节。

Consumer 端中有一个参数

session_timeout_ms (int): The timeout used to detect failures when
using Kafka's group management facilities. The consumer sends
periodic heartbeats to indicate its liveness to the broker. If
no heartbeats are received by the broker before the expiration of
this session timeout, then the broker will remove this consumer
from the group and initiate a rebalance. Note that the value must
be in the allowable range as configured in the broker configuration
by group.min.session.timeout.ms and group.max.session.timeout.ms.
Default:

Python 社区客户端 kafka-python 该参数的默认值是 10s 。

如果 Coordinator 在 10s 内没有收到 Group 下 Consumer 的实例心跳,就会认为这个 Consumer 已经挂了,就会触发 Rebalance。

除了 session.timeout.ms 还有一个控制发送心跳周期的参数 heartbeat.interval.ms

heartbeat_interval_ms (int): The expected time in milliseconds
between heartbeats to the consumer coordinator when using
Kafka's group management facilities. Heartbeats are used to ensure
that the consumer's session stays active and to facilitate
rebalancing when new consumers join or leave the group. The
value must be set lower than session_timeout_ms, but typically
should be set no higher than / of that value. It can be
adjusted even lower to control the expected time for normal
rebalances. Default:

heartbeat.interval.ms 的默认周期是 3s。这里社区客户端作者也描述得非常清楚(社区客户端的文档真的很好!)应该设置这个参数小于 session_timeout_ms ,通常大家会推荐一个公式为

heartbet_inverval_ms * 3 = session_timeout_ms

为什么要这么做?

因为 session_timeout_ms 到期如果 coordinator 没有收到心跳会认为客户端死了,如果按照上述的配置,期间客户端至少有三次时间访问到 coordinator 并且刷新过期时间。这样如果中间有 1 2 次因为网络问题没有发送成功的情况也可以一定程度避免。

心跳时间不超过 session_timeout_ms 的值,但是也不应该过长,过长可能会引起 Rebalance 的检测缓慢且失去效果。如果有一个 consumer 失效了,如果他无法再恢复我们要做的迅速让他被踢出 group 中。否则严重的话可能会引起 partitions 的数据倾斜,lag 也会越来越大。

除了

session.timeout.ms
heartbeat.interval.ms

还有两个参数从客户端角度去控制 Rebalance

max_poll_records (int): The maximum number of records returned in a
single call to :meth:`~kafka.KafkaConsumer.poll`. Default: 500 max_poll_interval_ms (int): The maximum delay between invocations of
:meth:`~kafka.KafkaConsumer.poll` when using consumer group
management. This places an upper bound on the amount of time that
the consumer can be idle before fetching more records. If
:meth:`~kafka.KafkaConsumer.poll` is not called before expiration
of this timeout, then the consumer is considered failed and the
group will rebalance in order to reassign the partitions to another
member. Default

max_poll_records 控制我们一次从 broker 请求多少数据过来,默认是 500 条。

max_poll_interval_ms 控制两次 poll 之间的时间间隔,如果我们请求过来的 500 条消息 300 s 都还没有消费完,没有继续调用 poll 那么 consumer 会自动向 coordinator 发出消息要求把自己踢出组内。coordinator 收到消息会开始新一轮的 Rebalance。

这里关于提交的时间还有一个需要被注意的地方。在社区 Kafka-Python 1.4.0 以下的版本中(broker 0.10.1 之前),心跳是跟随 poll 一起发送的。并没有启动一个 background 独立的线程去发送心跳包。这会造成一个什么问题呢?

之前如果我们拉取一批消息开始处理,他如果超过了设置的 session.timeout.ms 也就是 默认的 10s ,那么就会被触发 rebalance 。因为如果你不调用 poll 方法,你就无法发送心跳。Coodinator 无法收到心跳就会按照约定把你踢出 group 然后进行 rebalance .

Java 版本在发布了该功能之后,社区 kafka 版本从 1.4.0 之后开始支持了 background thread 处理心跳。详情可以参阅 reference。

请注意 max_poll_inerval_ms 这个参数是1.4.0版本以上的参数 1.3 的最后一个版本 1.3.5 不会有该参数的存在。所以如果使用 1.3.5 版本及其以下版本需要自己保证 单次拉取数据 的处理时间 < session_timeout_ms ,否则就会不停的触发 Rebalance 导致程序重复消费,严重可能引起死循环崩溃。

另外 1.3.5 及其以下版本中 session.timeout.ms 的默认值是 30s 并非现在版本的 10s ,时间比较长也是为了避免长时间处理引发的 Rebanlance ,老版本心跳的时间还是 3s 。

所以如果有要阻塞处理的任务,比如 retry 比如调用很多数据库操作,我们可以把 max_poll_interval_ms 的时间设得长一些,或者把需要处理的消息 max_poll_records 设置得少一些。这样至少不会因为正常的业务处理得慢而造成 kafka 频繁 Rebalance 从而引起其他的问题。

Rebalance 的过程

现在我们倒头回来聊聊 Rebalance 的过程。rebalance 的前提是当前消费者组 Coordinator 已经确定的情况。

在该 Consumer group 组有 Consumer 第一次请求的时候就会被分配 Coordinator broker .

计算方法为:

offset_partition = Math.abs(groupId.hashCode() % groupMetadataTopicPartitionCount) 
得到的分区的 leader 就是对应的 Coordinator
e.g.
public static void main(String[] args) {
String c = "illidan-c";
System.out.println(Math.abs(c.hashCode() % 10));
} 这里我的 nums.partitions 是 10 所以这里我得到的数是 9 然后查看 __consumer_offsets 9 的 leader 是 broker 0
  这里我去 broker 0 的 server.log 里面 tail -f 
然后我手动重启进程对其进行 rebalance 得到日志输出

[2020-01-06 12:11:23,808] INFO [GroupCoordinator 0]: Preparing to rebalance group illidan-c with old generation 158651 (__consumer_offsets-9) (kafka.coordinator.group.GroupCoordinator)
  [2020-01-06 12:11:30,650] INFO [GroupCoordinator 0]: Member kafka-python-1.4.7-d03089db-9df1-4464-bf06-4968df80bfa6 in group illidan-c has failed, removing it from the group (kafka.coordinator.group.GroupCoordinator)
  [2020-01-06 12:11:30,960] INFO [GroupCoordinator 0]: Member kafka-python-1.4.7-50c01dbc-eab5-41aa-823b-3530d32f845d in group illidan-c has failed, removing it from the group (kafka.coordinator.group.GroupCoordinator)
  [2020-01-06 12:11:30,960] INFO [GroupCoordinator 0]: Stabilized group illidan-c generation 158652 (__consumer_offsets-9) (kafka.coordinator.group.GroupCoordinator)
  [2020-01-06 12:11:30,979] INFO [GroupCoordinator 0]: Assignment received from leader for group illidan-c for generation 158652 (kafka.coordinator.group.GroupCoordinator)
  [2020-01-06 12:11:40,980] INFO [GroupCoordinator 0]: Member kafka-python-1.4.7-0094bb59-7cc1-4c80-9020-7c10c61bae98 in group illidan-c has failed, removing it from the group (kafka.coordinator.group.GroupCoordinator)
  [2020-01-06 12:11:40,980] INFO [GroupCoordinator 0]: Preparing to rebalance group illidan-c with old generation 158652 (__consumer_offsets-9) (kafka.coordinator.group.GroupCoordinator)

可以很清楚的看到即 consumer_offsets-9 作为 coordinator 的机器 broker 0 重新开始 Rebalance

之后该 group 组内的消费者都与此 coordinator 进行通信。

这里要再次强调一下。 关于 coordinator 是分为 broker 端 group coordinator 组件和消费者组这边的 coordinator leader 同步方案分配制定人的。

通常意义上我们说的 coordinator 一般指的是 broker 端的 group coordinator 组件。

Rebalance 的时候分为两步

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

2. Sync 这一步leader开始分配消费方案,即哪个consumer负责消费哪些topic的哪些partition。一旦完成分配,leader会将这个方案封装进SyncGroup请求中发给coordinator,非leader也会发SyncGroup请求,只是内容为空。coordinator接收到分配方案之后会把方案塞进SyncGroup的response中发给各个consumer。这样组内的所有成员就都知道自己应该消费哪些分区了。

上面提到的当 response 发回,那么我们需要等到所有消费者都确认了 SyncGroup 才会开始 response ,kafka 会将已经先收到的请求放到一个 purgatory 的地方。

总结一下就是当所有消费者成员都发送了 Join 加入组消息之后,broker 端 coordinator 会选举一个 consumer 担任 leader 角色。

这个 leader 要做的事情就是接收 coordinator 发送的成员信息及成员订阅 topic 信息并为这个消费者组内的消费者消费什么 parititons 制定分配方案和策略。

当方案制定完成之后,ledaer 会将方案通过 SyncGroup 请求发送回 coordinator ,这时非 leader 的 consumer 也会发送 SyncGroup 请求给 coordinator 然后 coordinator 将接收到的 leader 发来的分配方案通过 SyncGroup 的 response 发送回各 consumer 从而来完成 rebalance。

最后附上一张 rebalance 的状态机图片。

Reference:

https://www.cnblogs.com/huxi2b/p/6223228.html    Kafka消费组(consumer group)

https://matt33.com/2018/01/28/server-group-coordinator/    Kafka 源码解析之 GroupCoordinator 详解(十)

https://github.com/dpkp/kafka-python/releases

https://github.com/dpkp/kafka-python/pull/1266  KAFKA-3888 Use background thread to process consumer heartbeats

https://cwiki.apache.org/confluence/display/KAFKA/KIP-62%3A+Allow+consumer+to+send+heartbeats+from+a+background+thread  KIP-62: Allow consumer to send heartbeats from a background thread

什么是 Kafka Rebalance 以及关于 Rebalance Kafka-Python 社区客户端应该关注的地方的更多相关文章

  1. Kafka 0.8 Consumer Rebalance

    1 Rebalance时机 0.10kafka的rebalance条件 条件1:有新的consumer加入 条件2:旧的consumer挂了 条件3:coordinator挂了,集群选举出新的coor ...

  2. 揭秘Kafka高性能架构之道 - Kafka设计解析(六)

    原创文章,同步首发自作者个人博客.转载请务必在文章开头处以超链接形式注明出处http://www.jasongj.com/kafka/high_throughput/ 摘要 上一篇文章<Kafk ...

  3. 《Apache kafka实战》读书笔记-kafka集群监控工具

    <Apache kafka实战>读书笔记-kafka集群监控工具 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 如官网所述,Kafka使用基于yammer metric ...

  4. Kafka学习(三)-------- Kafka核心之Cosumer

    了解了什么是kafka( https://www.cnblogs.com/tree1123/p/11226880.html)以后 学习核心api之消费者,kafka的消费者经过几次版本变化,特别容易混 ...

  5. Kafka深入理解-1:Kafka高效的文件存储设计

    文章摘自:美团点评技术团队  Kafka文件存储机制那些事 Kafka是什么 Kafka是最初由Linkedin公司开发,是一个分布式.分区的.多副本的.多订阅者,基于zookeeper协调的分布式日 ...

  6. Kafka【第一篇】Kafka集群搭建

    Kafka初识 1.Kafka使用背景 在我们大量使用分布式数据库.分布式计算集群的时候,是否会遇到这样的一些问题: 我们想分析下用户行为(pageviews),以便我们设计出更好的广告位 我想对用户 ...

  7. 【转】Kafka实战-Flume到Kafka

    Kafka实战-Flume到Kafka Kafka   2015-07-03 08:46:24 发布 您的评价:       0.0   收藏     2收藏 1.概述 前面给大家介绍了整个Kafka ...

  8. kafka知识体系-kafka设计和原理分析-kafka文件存储机制

    kafka文件存储机制 topic中partition存储分布 假设实验环境中Kafka集群只有一个broker,xxx/message-folder为数据文件存储根目录,在Kafka broker中 ...

  9. Kafka相关内容总结(Kafka集群搭建手记)

    简介 Kafka is a distributed,partitioned,replicated commit logservice.它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是 ...

随机推荐

  1. Delphi中AssignFile函数

    procedure TForm1.SaveLog(sFlag:string;MSG:string);var QF1:Textfile;         ----声明文本文件类型 Qfiletmp,sP ...

  2. MVC运行机制[转]

    原:http://www.cnblogs.com/jyan/archive/2012/06/29/2569566.html#3122335 ASP.NET是一种建立动态Web应用程序的技术.它是.NE ...

  3. jQuery实现点击图片简单放大效果

    一.HTML代码如下: <img class="comment_pics" width="50px" height="50px" sr ...

  4. js 动态创建 全局变量(转载)

    转载来源 https://blog.csdn.net/stevenzhong900610/article/details/40857087 https://www.jb51.net/article/8 ...

  5. Java开发环境之ActiveMQ

    查看更多Java开发环境配置,请点击<Java开发环境配置大全> 柒章:ActiveMQ安装教程 1)去官网下载ActiveMQ安装包 http://activemq.apache.org ...

  6. Web数据库架构

    Web服务器的基本操作如图下图所示: 这个系统由两个对象组成:一个Web浏览器和一个Web服务器.它们之间需要通信连接.Web浏览器向服务器发出请求.服务器返回一个响应.这种架构非常适合服务器发布静态 ...

  7. C语言开发具有可变长参数的函数的方法

    学习交流可加 微信读者交流①群 (添加微信:coderAllen) 程序员技术QQ交流①群:736386324 --- 前提:ANSI C 为了提高可移植性, 通过头文件stdarg.h提供了一组方便 ...

  8. HTML&CSS基础-html的图片标签

    HTML&CSS基础-html的图片标签 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.如下图所示,准备一张图片,存放路径和html文件在同一目录 二.HTML源代码 ...

  9. typeScript学习随笔(一)

    TypeScript学习随笔(一) 这么久了还不没好好学习哈这么火的ts,边学边练边记吧! 啥子是TypeScript  TypeScript 是 JavaScript 的一个超集,支持 es6 标准 ...

  10. JMeter压测时报“内存不足”故障的9个简单解决方案

    Test failed! java.lang.OutOfMemoryError: Java heap space 测试失败了!java.lang.OutOfMemoryError:Java堆空间 在不 ...