kafka-python 1.4.6 版本触发的一个 rebalance 问题
在使用了最新版的 kafka-python 1.4.6 在 broker 对 topic 进行默认配置的情况下报出类似错误
CommitFailedError
CommitFailedError: Commit cannot be completed since the group has already
rebalanced and assigned the partitions to another member.
This means that the time between subsequent calls to poll()
was longer than the configured max_poll_interval_ms, which
typically implies that the poll loop is spending too much
time message processing. You can address this either by
increasing the rebalance timeout with max_poll_interval_ms,
or by reducing the maximum size of batches returned in poll()
with max_poll_records.
这里要申明一点,在 1.4.0 以上的 kafka-python 版本使用了独立的心跳线程去上报心跳。
这里报错大概表达的意思是 无法在默认 300000ms 中完成处理操作。我们通常会一次性 poll 拉默认 500 条数据下来。我们需要在 300s 中完成 500 条数据的处理。如果不能完成的话就可能会触发这个问题。
因为这个报错的提示写得非常清楚,所以我们先按这个方向去尝试处理这个问题。首先调高了我们的 max_poll_interval_ms 的时间,但是无效。
然后 records 的条数减少,依然无效,该报错还是会报错。这不禁让我怀疑触发这个问题的是否并非这里报错建议的那些地方。
所以我把目光放到了 broker 日志那边去,想看下到底是因为什么原因导致爆出类似错误。
在日志上发现了一些日志,对应的 consumer 在反复的 rebalance:
[-- ::,] INFO [GroupCoordinator ]: Member kafka-python-1.4.-05ed83f1-aa90--b097-4cf467598082 in group sync_group_20180321 has failed, removing it from the group (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Stabilized group sync_group_20180321 generation (__consumer_offsets-) (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Member kafka-python-1.4.-f7826720-fef7-4b02--d1f38065c2fe in group sync_group_20180321 has failed, removing it from the group (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Preparing to rebalance group sync_group_20180321 with old generation (__consumer_offsets-) (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Member kafka-python-1.4.-ac5f6aff--4e67-a529-31674c72b1e4 in group sync_group_20180321 has failed, removing it from the group (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Stabilized group sync_group_20180321 generation (__consumer_offsets-) (kafka.coordinator.group.GroupCoordinator)
[-- ::,] INFO [GroupCoordinator ]: Assignment received from leader for group sync_group_20180321 for generation (kafka.coordinator.group.GroupCoordinator)
参考 sentry 打出来的错误,我们可以认为这和 sentry 爆出来的问题有直接关系。因此我们要从另外一个角度去思考一下为什么我的 max_poll_interval_ms 已经调高并且每次拉取处理条数降低却依然会报出此问题,并且频繁触发 rebalance 。
kafka-python 在 1.4.0 版本分离了心跳线程和 poll 主线程。我的第一反应就是会不会因为 poll 线程阻塞了心跳线程的切换,或者引起了某种死锁从而导致心跳线程无法正确的发起心跳。最后导致 broker 认为 group 已经死亡然后主动触发了 rebalance .
然后我去 kafka-python 的 gihub 搜索了一下类似问题,马上就发现了有不少人都有这个问题。
https://github.com/dpkp/kafka-python/issues/1418
从中找到一些有趣的信息,比如来自 vimal3271 的测试
I am seeing consumer rebalances even if there is no messages to consume. Start three consumers in a group and send some messages to topic and after that stop the producer. The consumer will start seeing rebalances after -6mins.
Sample code here:
https://stackoverflow.com/questions/54908902/kafka-consumer-rebalance-occurs-even-if-there-is-no-message-to-consume
他说即使在没有消息可以消费的情况下,也可以看到 kafka consumer 在过了 5 - 6 mins 之后开启了 rebalance 。
这就跟我们的问题非常相似,我们并不是 process 的过程消耗的时间过长而触发了 rebalance 而是有可能是因为消费得太快,导致有些消费者处于 空 poll 的状态从而阻塞了心跳线程。客观来说,我目前还会报出这个问题的 topic 有多达 50 个partitions,我开启了5个消费者对其进行消费,平均一个消费者需要消费 10 个parititons 。如果有某个 partitions 长期没有消费过来我们可能会被阻塞在那里最终导致 heartbeat 超时。 1.4.6 的客户端默认 10s 没心跳就超时,而发送间隔仅为 3s 。也就是连续三个周期没有发送就超时了。
下面看到 dpkp 的一个回复,表达了有可能就是被 poll 主线程阻塞,的问题,并且有 workaround 可以用来避免这种情况:
vimal: thanks for posting. I believe you may be hitting lock contention between an idle client.poll -- which can block and hold the client lock for the entire request_timeout_ms -- and the attempt by the heartbeat thread to send a new request. It seems to me that we may need to use KafkaClient.wakeup() to make sure that the polling thread drops the lock if/when we need to send a request from a different thread. This shouldn't be an issue when messages are flowing through your topics at a steady rate. If this is just a test environment, and you expect your production environment to have more steady live data, then you could just ignore the error in testing. But if you are managing a topic w/ very low traffic -- delays of minutes between consecutive messages, for example -- you might try to reduce the request_timeout_ms to something closer to the heartbeat_interval_ms, which should prevent the read side from blocking for much longer than the heartbeat timeout. But note that other timeouts may also need to change (max_poll_interval_ms and session_timeout_ms perhaps). Another workaround might be to reduce metadata_max_age_ms to something close / equal to your heartbeat_timeout_ms. This will cause more frequent metadata requests, but should unblock the send side when there is no socket data available for reads.
dpkp 的观点在于,如果我们数据发送过来的频率是稳定的,消费者是正好可以消费完队列里面的信息的情况的时候,不应该出现这样的问题。出现这样的问题与我们预期和看到报错的情况可能恰恰相反,不是我们消费得太慢,而是我们消费得太快,并且生产者发送消息的频率过低导致的。在 poll 不到消息的时候,主线程可能会面临阻塞,而无法及时切换到心跳线程进行心跳的发送,最终导致了这个问题。
他给到一个 trick 的方法来解决这个问题,当面临这种情况的时候我们可以把 metadata_max_age_ms 调整到和心跳发送频率差不多 close / equal to our heartbeat_timeout_ms.
发送 metadata_request 会解除我们发送端的阻塞,从而达到抑制死锁的效果。
self.kafka = kafka.KafkaConsumer(
auto_offset_reset=auto_offset_reset,
bootstrap_servers=['10.171.97.1:9092', '10.163.13.219:9092', '10.170.249.122:9092'],
group_id=group_id,
metadata_max_age_ms=metadata_max_age_ms
)
self.kafka.subscribe(topics)
尝试补充了 metadata_max_age_ms 大约 3000 ms ,这个问题得到了很大程度的解决和缓解。
既然确定了可能是因为消费太快,然后生产慢导致的主线程锁住的问题,剩下可以验证一下是否真的是这样。尝试打日志看一下切换线程发送心跳的情况可以来确认该问题是否如此。
另外看代码发现 poll 主线程在 poll 之前会默认会进行 wakeup() 但是在 1.4.6里面也因为之前的 某个 bug 而默认关闭了,不知道是否有影响,等后续测试之后补上。
Reference:
https://github.com/dpkp/kafka-python/issues/1418 Heartbeat failed for group xxxWorker because it is rebalancing
https://github.com/dpkp/kafka-python/issues/1760 [1.4.5] KafkaProducer raises KafkaTimeoutError when attempting wakeup()
https://www.cnblogs.com/huxi2b/p/6815797.html Kafka 0.11版本新功能介绍 —— 空消费组延时rebalance
kafka-python 1.4.6 版本触发的一个 rebalance 问题的更多相关文章
- 【原创】Windows平台搭建Kafka源代码开发环境(Eclipse版本)
最近在研究Kafka源代码,需要自己搭建一个开发环境.官网上给出的提示略显简单,照着做了一遍也碰到了一些问题.特此记录下来. 开发环境: Oracle Java 1.7_u71 + Eclipse 4 ...
- 升级python到2.7版本pip不可用
升级python到2.7版本pip不可用 [root@localhost pip-7.1.2]# pip Traceback (most recent call last): File "/ ...
- python和numpy的版本、安装位置
命令行下查看python和numpy的版本和安装位置 1.查看python版本 方法一: python -V 注意:‘-V‘中‘V’为大写字母,只有一个‘-’ 方法二: python --versio ...
- 升级python的sqlite库版本
今天了解了一下用python获取chrome cookie信息,在研究的过程中,发现打开数据库失败,后来调查了一下发现是由于sqlite3库太老的缘故,起码需要3.8以上,然后看了一下python 2 ...
- Maya Max python PySide集成 shiboken版本对应关系
Maya_Max _python_PySide集成_shiboken版本对应关系 1.如何查看 Maya Max 集成的 Python版本: Maya:在 Maya 的安装目录下的 bin 文件夹中找 ...
- 如何查看安装python和numpy的版本
命令行下查看python和numpy的版本和安装位置 1.查看python版本 方法一: python -V 注意:‘-V‘中‘V’为大写字母,只有一个‘-’ 方法二: python --versio ...
- 命令行下查看python和numpy的版本和安装位置
命令行下查看python和numpy的版本和安装位置 1.查看python版本 方法一: python -V 注意:‘-V‘中‘V’为大写字母,只有一个‘-’ 方法二: python --versio ...
- 【转】python 2.6.6升级到python 2.7.x版本的方法
1.下载python2.7.x wget https://www.python.org/ftp/python/2.7.6/Python-2.7.6.tgz 2.解压并编译安装 tar -zxvf Py ...
- 2个版本并存的python使用新的版本安装django的方法
2个版本并存的python使用新的版本安装django的方法 默认是使用 pip install django 最新版的django会提示 要求python版本3.4以上,系统默认的版本是2.7.5 ...
随机推荐
- Visual Studio 2019 XAML Hot Reload功能介绍
Visual Studio 2019提供了XAML Hot Reload功能,这个功能可以让WPF程序运行以后仍然可以修改XAML代码,并实时显示. XAML Hot Reload功能在Blend F ...
- C# vb .net实现颜色替换效果滤镜
在.net中,如何简单快捷地实现Photoshop滤镜组中的颜色替换效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第 ...
- 3.matplotlib绘制条形图
plt.bar() # coding=utf-8 from matplotlib import pyplot as plt from matplotlib import font_manager my ...
- golang中生成读取二维码(skip2/go-qrcode和boombuler/barcode,tuotoo/qrcode)
1 引言 在github上有好用golan二维码生成和读取库,两个生成二维码的qrcode库和一个读取qrcode库. skip2/go-qrcode生成二维码,github地址:https://g ...
- 小知识:讲述Linux命令别名与资源文件的区别
别名 别名是命令的快捷方式.为那些需要经常执行,但需要很长时间输入的长命令创建快捷方式很有用.语法是: alias ppp='ping www.baidu.com' 它们并不总是用来缩短长命令.重要的 ...
- pandas-15 df['one_col'].apply()方法的用法
pandas-15 df['one_col'].apply()方法的用法 apply有点像map的用法,可以传入一个函数. 如:df['A'].apply(str.upper) import nump ...
- JS构造函数new的过程
造函数其实和普通函数本质上并无区别,唯一的区别有两个: 函数首字母大写,这个区别只是约定俗成的,便于区分.你实在要小写定义构造函数也完全没问题,所以这个区别可以忽略. 构造函数的调用需要用new操作符 ...
- nodeJS实现简易爬虫
nodeJS实现简易爬虫 需求:使用nodeJS爬取昵图网某个分类下的图片并存入本地 运用nodeJS自带系统模块http.fs 示例代码: var http =require('http'); va ...
- JS中的七大数据类型
js中有7种数据类型,包括五种基本数据类型(Number,String,Boolean,Undefined,Null),和一种复杂数据类型(Object)以及es6语法新增的Symbol数据类型 es ...
- Node: Process模块 (Features of Process)
Process模块 (Features of Process) process is a global variable which indicates the current Node.js pro ...