重平衡
背景:
假如你是一家公司的老板,手下有三名员工张三、李四、王五,现在你有三项工作A、B、C,正好安排给三人。
过了一个月后,员工王五离开了公司。这个时候只剩下两名员工,你只能把原先王五安排的工作交给李四。
又过了一个月,公司进来了员工小明、小红,但是因为每项工作都是独立的,所以没有新的任务分配给小红
后来你发现这样的任务分配不合理,你将各项工作进一步细化,保证每个员工都能分配到工作
原先的三项工作被细化成了九项工作,这样每个人就都有活干了,但是可能有的人要承担额外的工作(平均分配不了)
设计
对于Kafka来说,背景中的公司可以理解为一个主题,公司里的每个工作就是主题的每个分区。事实上一个消费者组可以订阅不同的主题,但是一个分区只能被一个消费者消费,也就是说一项“工作”只能被分配给一名“员工”,这样的设计是考虑是:
- 将分区均衡分配给每个消费者
- 保证顺序消费(一个分区内的消息是有序的,且只能被一名消费者消费)
分区分配算法
Kafka提供了三种分配策略,分别是RANGE,Round Robin以及Sticky
在触发重平衡的过程中,消费者是无法进行消费的,所以在生产环境中,我们往往需要避免这种情况的发生
相关参数
与重平衡相关参数:
- session.timeout.ms 心跳超时时间,默认10s
- heartbeat.interval.ms 发送心跳频率,默认3s
- max.poll.interval.ms 两次拉取消息之间间隔,默认值5分钟
- max.poll.records 一次性最大拉取多少条消息,默认值500条
对于session.timeout.ms与heartbeat.interval.ms,开源官方有这样一条注释:
The expected time between heartbeats to the consumer coordinator when using 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 1/3 of that value. It can be adjusted even lower to control the expected time for normal rebalances.
建议值是将session.timeout.ms设置为不高于heartbeat.interval.ms三分之一值。这样做的原因是如果你将心跳时间设置得过长,那么broker就无法检测到消费者的异常。如果时间设置得过短,那么可能短暂的网络抖动就可能会触发rebalance,同时也会频繁的心跳也会造成网络资源的浪费。
重平衡场景排查指南
Kafka的心跳线程是consumer与broker之间的心跳检查,如果消费者发现和broker之间的心跳超过了session.timeout.ms,那么消费者会阻塞并且进行重试。如果两次拉取消息的时间超过了max.poll.interval.ms , 那么即使消费者与客户端的心跳线程仍在保持,但是broker会认为客户端处于livelock状态,也会触发rebalance
出现Rebalance原因有可能以下几种:
- 一次性拉取消息过多,处理能力不够
- 解决方法:减少max.poll.records参数
- 代码自旋/死锁
- 在消费者拉取消息后,在代码逻辑处理上可能有自旋或等待锁操作,导致消息处理过长时间
- 进程GC时间过长,导致心跳线程无法发送(或频繁full gc)
- 等待数据库锁
- 拉取消息后,可能存在写库/查询操作,可能使数据库死锁或插入/查询时间长导致消息处慢
- 线程池满,参数配置有问题(核心线程数、最大线程数)
- 存在耗时I/O操作(网络IO、磁盘IO)