今天在服务日志中观察数据的消费情况时,发现了一个如下的警告,而且每隔几秒就会出现一次,虽然只是个警告,

 


Auto offset commit failed for group order_group: 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.


但是看到auto offset commit failed时我就不淡定了,不难看出是自动提交offset失败了,我们都知道,kafka的数据更新消费都是通过在zookeeper中标记一个偏移量(offset)来记录每个分区的消费位置,所以一旦offset更新失败,不难想象肯定会出现重复消费数据的问题!

通过以上信息分析大概意思是:kafka消费者在处理消息时,在指定时间内(session.time.out)没有处理完,consumer coordinator会由于没有接受到心跳而挂掉,导致自动提交offset失败,因此就会像日志中所说的发生rebalanced(重平衡即重新分配partition给客户端),而之前提交的offset已经失败了,所以重新分配的客户端又会消费之前的数据,接着consumer重新消费,又出现了消费超时,无限循环下去。

解决方法:

将enable.auto.commit设置成false,即不采用自动提交方式;

 

由于使用了spring-kafka,禁止kafka-client自动提交offset,因为就是之前的自动提交失败,导致offset永远没更新,从而转向使用spring-kafka的offset提交机制。

1)如果auto.commit关掉的话,spring-kafka会启动一个invoker,这个invoker的目的就是启动一个线程去消费数据,他消费的数据不是直接从kafka里面直接取的,那么他消费的数据从哪里来呢?他是从一个spring-kafka自己创建的阻塞队列里面取的。

2)然后会进入一个循环,从源代码中可以看到如果auto.commit被关掉的话, 他会先把之前处理过的数据先进行提交offset,然后再去从kafka里面取数据。

3)然后把取到的数据丢给上面提到的阻塞列队,由上面创建的线程去消费,并且如果阻塞队列满了导致取到的数据塞不进去的话,spring-kafka会调用kafka的pause方法,则consumer会停止从kafka里面继续再拿数据。

4)接着spring-kafka还会处理一些异常的情况,比如失败之后是不是需要commit offset这样的逻辑