一.前言
最近项目中替换了我以前使用的string cloud stream kafka后,改为了spring kafka。当大批量数据进来的时候,就会频繁出现消息堆积现象,然后查询库里有很多条重复数据。典型的kafka重复消费问题。
二.现象
elk中发现,"Auto offset commit failed"
频繁出现,自动提交失败。
检查kafka配置后,发现,自动提交是开启的。
那么问题来了,为什么自动提交开启了,并发来的时候,就会出现提交失败呢?就会重复消费呢?
再仔细看一下,上面是因为两次poll()的时间间隔大于配置的session.timeout.ms,根本原因是处理时间太长,大于设定的session.timeout.ms。如果长时间不调用poll()方法,集群会认为该消费者已经挂掉了,就不会让它提交偏移量了,这样就会造成重复消费数据。
poll() API 主要是判断consumer是否还活着,只要我们持续调用poll(),消费者就会存活在自己所在的group中,并且持续的消费指定partition的消息。
底层是这么做的:消费者向server持续发送心跳,如果一个时间段(session.timeout.ms)consumer挂掉或是不能发送心跳,这个消费者会被认为是挂掉了,这个Partition也会被重新分配给其他consumer。
三.一点参数配置
5.session.timeout.ms 和 max.poll.interval.ms的区别
Assuming we are talking about Kafka 0.10.1.0 or upwards where each consumer instance employs two threads to function. One is user thread from which poll is called; the other is heartbeat thread that specially takes care of heartbeat things.
session.timeout.ms is for heartbeat thread. If coordinator fails to get any heartbeat from a consumer before this time interval elapsed, it marks consumer as failed and triggers a new round of rebalance.
max.poll.interval.ms is for user thread. If message processing logic is too heavy to cost larger than this time interval, coordinator explicitly have the consumer leave the group and also triggers a new round of rebalance.
heartbeat.interval.ms is used to have other healthy consumers aware of the rebalance much faster. If coordinator triggers a rebalance, other consumers will only know of this by receiving the heartbeat response with REBALANCE_IN_PROGRESS exception encapsulated. Quicker the heartbeat request is sent, faster the consumer knows it needs to rejoin the group.
Suggested values:
session.timeout.ms : a relatively low value, 10 seconds for instance.
max.poll.interval.ms: based on your processing requirements
heartbeat.interval.ms: a relatively low value, better 1/3 of the session.timeout.ms
- max.poll.interval.ms:消费者最大心跳时间间隔
- maximum size of batches:消费者每次获取消息的个数
四.解决方案
思路:
- 调大max.poll.interval.ms(两次poll方法最大时间间隔),默认时间为300000ms
- 调小max.poll.records(一次最多处理的记录数量),默认500
- 启动多个线程并行处理数据,但要注意处理完一批消息后才能提交offset,然后进行下次的poll(会用到CountDownLatch)
- 关闭自动提交