记一次线上kafka一直rebalance故障

原文地址:https://www.jianshu.com/p/271f88f06eb3

今天我司线上kafka消息代理出现错误日志,异常rebalance,而且平均间隔2到3分钟就会rebalance一次,分析日志发现比较严重。错误日志如下

08-09 11:01:11 131 pool-7-thread-3 ERROR [] - 
commit failed 
org.apache.kafka.clients.consumer.CommitFailedException: 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 session timeout or by reducing the maximum size of batches returned in poll() with max.poll.records.
        at org.apache.kafka.clients.consumer.internals.ConsumerCoordinator.sendOffsetCommitRequest(ConsumerCoordinator.java:713) ~[MsgAgent-jar-with-dependencies.jar:na]
        at org.apache.kafka.clients.consumer.internals.ConsumerCoordinator.commitOffsetsSync(ConsumerCoordinator.java:596) ~[MsgAgent-jar-with-dependencies.jar:na]
        at org.apache.kafka.clients.consumer.KafkaConsumer.commitSync(KafkaConsumer.java:1218) ~[MsgAgent-jar-with-dependencies.jar:na]
        at com.today.eventbus.common.MsgConsumer.run(MsgConsumer.java:121) ~[MsgAgent-jar-with-dependencies.jar:na]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_161]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_161]
        at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]

这个错误的意思是,消费者在处理完一批poll的消息后,在同步提交偏移量给broker时报的错。初步分析日志是由于当前消费者线程消费的分区已经被broker给回收了,因为kafka认为这个消费者死了,那么为什么呢?

分析问题

这里就涉及到问题是消费者在创建时会有一个属性max.poll.interval.ms
该属性意思为kafka消费者在每一轮poll()调用之间的最大延迟,消费者在获取更多记录之前可以空闲的时间量的上限。如果此超时时间期满之前poll()没有被再次调用,则消费者被视为失败,并且分组将重新平衡,以便将分区重新分配给别的成员。

如上图,在while循环里,我们会循环调用poll拉取broker中的最新消息。每次拉取后,会有一段处理时长,处理完成后,会进行下一轮poll。引入该配置的用途是,限制两次poll之间的间隔,消息处理逻辑太重,每一条消息处理时间较长,但是在这次poll()到下一轮poll()时间不能超过该配置间隔,协调器会明确地让使用者离开组,并触发新一轮的再平衡。
max.poll.interval.ms默认间隔时间为300s

分析日志

从日志中我们能看到poll量有时能够达到250多条

 一次性拉取250多条消息进行消费,而由于每一条消息都有一定的处理逻辑,根据以往的日志分析,每条消息平均在500ms内就能处理完成。然而,我们今天查到有两条消息处理时间超过了1分钟。

消息处理日志1

08-09 08:50:05 430 pool-7-thread-3 INFO [] - [RestKafkaConsumer] receive message (收到消息,准备过滤,然后处理), topic: member_1.0.0_event ,partition: 0 ,offset: 1504617
08-09 08:50:05 431 pool-7-thread-3 INFO [] - [RestKafkaConsumer]:解析消息成功,准备请求调用!
08-09 08:51:05 801 pool-7-thread-3 INFO [] - [HttpClient]:response code: {"status":200,"data":{"goodsSendRes":{"status":400,"info":"指>定商品送没有可用的营销活动--老pos机"},"fullAmountSendRes":{"status":400,"info":"满额送没有可用的营销活动--老pos机"}},"info":"发券流程执
行成功"}, event:com.today.api.member.events.ConsumeFullEvent, url:https://wechat-lite.today36524.com/api/dapeng/subscribe/index,event内
容:{"id":36305914,"score":16,"orderPrice":15.9,"payTime":1533775401000,"thirdTransId":"4200000160201808

消息处理日志2

08-09 08:51:32 450 pool-7-thread-3 INFO [] - [RestKafkaConsumer] receive message (收到消息,准备过滤,然后处理), topic: member_1.0.0_event ,partition: 0 ,offset: 1504674
08-09 08:51:32 450 pool-7-thread-3 INFO [] - [RestKafkaConsumer]:解析消息成功,准备请求调用!
08-09 08:52:32 843 pool-7-thread-3 INFO [] - [HttpClient]:response code: {"status":200,"data":{"goodsSendRes":{"status":400,"info":"指>定商品送没有可用的营销活动--老pos机"},"fullAmountSendRes":{"status":400,"info":"满额送没有可用的营销活动--老pos机"}},"info":"发券流程执
行成功"}, event:com.today.api.member.events.ConsumeFullEvent, url:https://wechat-lite.today36524.com/api/dapeng/subscribe/index,event内
容:{"id":36306061,"score":3,"orderPrice":3.0,"payTime":1533775482000,"thirdTransId":"420000016320180809

我们看到消息消费时间都超过了1分钟。

分析原因

如下是我们消费者处理逻辑(省略部分代码)

while (isRunning) {
            ConsumerRecords<KEY, VALUE> records = consumer.poll(100);
            if (records != null && records.count() > 0) {
           
            for (ConsumerRecord<KEY, VALUE> record : records) {
                dealMessage(bizConsumer, record.value());
                try {
                    //records记录全部完成后,才提交
                      consumer.commitSync();
                } catch (CommitFailedException e) {
                      logger.error("commit failed,will break this for loop", e);
                        break;
                }
            }
}

poll()方法该方法轮询返回消息集,调用一次可以获取一批消息。

kafkaConsumer调用一次轮询方法只是拉取一次消息。客户端为了不断拉取消息,会用一个外部循环不断调用消费者的轮询方法。每次轮询到消息,在处理完这一批消息后,才会继续下一次轮询。但如果一次轮询返回的结构没办法及时处理完成,会有什么后果呢?服务端约定了和客户端max.poll.interval.ms,两次poll最大间隔。如果客户端处理一批消息花费的时间超过了这个限制时间,服务端可能就会把消费者客户端移除掉,并触发rebalance

拉取偏移量与提交偏移量

kafka的偏移量(offset)是由消费者进行管理的,偏移量有两种,拉取偏移量(position)与提交偏移量(committed)。拉取偏移量代表当前消费者分区消费进度。每次消息消费后,需要提交偏移量。在提交偏移量时,kafka会使用拉取偏移量的值作为分区的提交偏移量发送给协调者。
如果没有提交偏移量,下一次消费者重新与broker连接后,会从当前消费者group已提交到broker的偏移量处开始消费。
所以,问题就在这里,当我们处理消息时间太长时,已经被broker剔除,提交偏移量又会报错。所以拉取偏移量没有提交到broker,分区又rebalance。下一次重新分配分区时,消费者会从最新的已提交偏移量处开始消费。这里就出现了重复消费的问题。

解决方案

1.增加max.poll.interval.ms处理时长

kafka消费者默认此间隔时长为300s

max.poll.interval.ms=300

2.设置分区拉取阈值

kafkaConsumer调用一次轮询方法只是拉取一次消息。客户端为了不断拉取消息,会用一个外部循环不断调用轮询方法poll()。每次轮询后,在处理完这一批消息后,才会继续下一次的轮询。

max.poll.records = 50

3.poll到的消息,处理完一条就提交一条,当出现提交失败时,马上跳出循环,这时候kafka就会进行rebalance,下一次会继续从当前offset进行消费。

while (isRunning) {
            ConsumerRecords<KEY, VALUE> records = consumer.poll(100);
            if (records != null && records.count() > 0) {
           
            for (ConsumerRecord<KEY, VALUE> record : records) {
                dealMessage(bizConsumer, record.value());
                try {
                    //records记录全部完成后,才提交
                      consumer.commitSync();
                } catch (CommitFailedException e) {
                      logger.error("commit failed,will break this for loop", e);
                        break;
                }
            }
}

附录 查询日志 某个topic的 partition 的rebalance过程

时间revoked positionrevoked committed时间assigned
08:53:211508667150850908:57:171508509
09:16:311509187150850909:21:021508509
09:23:181509323150850909:26:021508509
09:35:161508509150850909:36:031508509
09:36:211508509150850909:41:031508509
09:42:151509323150850909:46:031508509
09:47:191508509150850909:51:031508509
09:55:041509323150932309:56:031509323
多余消费被回滚重复消费10:01:031509323
10:02:201510205150932310:06:031509323
10:07:291509323150932310:08:351509323
10:24:431509693150969310:25:181509693
10:28:381510604151060410:35:181510604
10:36:371511556151060410:40:181510604
10:54:261511592151159210:54:321511592
---10:59:321511979
11:01:111512178151217811:03:401512178
11:04:351512245151224511:08:491512245
11:12:471512407151240711:12:491512407
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Kafka的rebalance会影响消费者组内各个消费者的分区分配,从而影响消费者的消费速度和消费顺序。 当消费者加入或离开消费者组时,Kafka会触发rebalance操作,重新分配消费者组内各个消费者所消费的分区。这个过程可能会导致一些消费者需要重新连接分区,从而影响消费速度;同时也可能会导致某些消息的消费顺序发生变化,因为消费者之间重新分配了分区。 因此,在使用Kafka时,需要考虑好消费者组的设置和rebalance的触发条件,以及如何处理rebalance操作对消费者的影响。 ### 回答2: Kafka的rebalance(重新平衡)是指在Kafka集群中添加或删除broker或者消费者时,自动重新分配分区给消费者的过程。它主要影响了Kafka集群的可用性、消费者的负载均衡和消费顺序。 首先,rebalance会影响Kafka集群的可用性。当添加或删除broker时,集群需要重新分配分区以保持数据的冗余备份,这可能导致集群的可用性下降,在重新平衡期间,某些分区可能无法访问。 其次,rebalance会影响消费者的负载均衡。消费者组内的不同消费者订阅同一个主题的不同分区,rebalance会重新分配分区给消费者,以确保每个消费者负责处理大致相同数量的分区。这样可以确保消费者之间的负载均衡,避免某个消费者过载而导致延迟增加,同时还能充分利用集群的吞吐量。 另外,rebalance还会影响消费顺序。Kafka保证同一个分区中的消息顺序,但在rebalance之后,分区被重新分配给其他消费者,这可能导致之前已经按顺序消费的消息重新分配给新的消费者,打乱消息的顺序。因此,消费者需要在rebalance之后重新定位到正确的位置,以确保顺序消费。 总之,Kafka的rebalance对可用性、负载均衡和消费顺序都有一定程度的影响。它在集群扩容、缩容或消费者组内的消费者变化时自动进行,为Kafka提供了高可靠性和可伸缩性的支持。 ### 回答3: Kafka的rebalance指的是Kafka集群重新分配partition给不同的consumer,主要影响如下: 1. 分区的重新分配: 当某个consumer加入或离开Kafka集群时,rebalance会导致已有partition的重新分配。这可能会导致消费者重复消费或漏掉某些消息。重新分配带来的分区变化会影响到消费者的消息处理。 2. 消费者群组的重新平衡: 当消费者群组内的消费者数量发生变化时,rebalance会重新分配分区给各个消费者,以达到负载均衡。消费者的重新平衡操作可能会导致消费者停止消费一段时间,从而影响整个消费群组的消息处理能力和延迟。 3. 消费者的会话过期: 在Kafka中,消费者与broker保持心跳连接来维持会话状态。如果一个消费者长时间没有发送心跳,broker会将该消费者视为失效,从而触发rebalance消费者的会话过期会导致分区的重新分配,从而影响消费者停止消费和重新分配分区。 4. 效率和性能问题: rebalance涉及到大量partition的重新分配和消费者的重新注册,会导致一定的性能损耗和网络传输开销。尤其在集群中有大量的分区和消费者时,rebalance可能会导致大量的网络流量和延迟增加。 综上所述,Kafka的rebalance可以影响消费者群组中消息的分配和消费,引起消息重复或丢失的问题,并可能导致消费者停止消费一段时间。因此,在设计Kafka应用程序时,需要合理规划消费者群组和分区的数量,以减少rebalance对系统运行的影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值