kafka参数解析

一、kafka参数解析
一个消费者可以消费同一个topic的多个分区,但是一个分区不能被同一个组下的多个消费者消费。同一个组下有多个消费者并发消费同一个topic时,要注意设置的消费者并发个数一定要小于等于topic的分区数,不然会有空置的线程没有分区可以消费,并发的时候根据分区数和消费者的个数来分配每个消费者消费几个分区,消费者可以消费一个或多个分区。

#消费端自动提交
kafka.enable.auto.commit=true
#它指定了一个阈值---10秒,在这个阈值内如果服务端coordinator未收到consumer的任何消息,那coordinator就认为consumer挂了,把它踢出消费分组。
kafka.session.timeout=6000
#提交间隙
kafka.auto.commit.interval=100
#auto.offset.reset 消费策略常用的两种取值
#earliest 当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,从头开始消费
#latest 当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据

当新加了分区时,此时如果消费端没有起来,当产生一定量的数据后,起了消费,这时如果用的latest,latest会从拉起的那一个开始消费后面的数据,前面的就丢失了,如果用的earliest会从头消费。

如果是新加了topic,但消费没起来,等产生了一定数据后,latest会从消费拉起的那刻起消费新产生的数据,earliest会重头消费。
如果每个topic都被消费过并且分区都提交了offert,则不管用latest还是earliest都会从上次提交的offert开始消费,所以会消费积压的数据。

kafka.auto.offset.reset=latest

 

#消费者并发个数,设置为几同一个消费组下就有几个消费者并发消费同一个topic,一个消费者就是一个线程。并发数要小于topic的分区数,避免有空线程无分区可消费。

每个消费者会拉起三个线程,ThreadPooltaskSchedule 控制消费者的定时任务,
kafka-coordinator-heartbeat-thread 消费者和服务端的心跳线程,
org.springframework.kafka.KafkaListenerEndpointContainer 消费者poll数据的线程。

kafka.concurrency=2
kafka.key.deserializer=org.apache.kafka.common.serialization.ByteArrayDeserializer
kafka.value.deserializer=org.apache.kafka.common.serialization.ByteArrayDeserializer

 


session.timeout.ms 和 heartbeat.interval.ms及 max.poll.interval.ms 三个参数的意义:


一个消费者有两个线程,消费者和服务端GroupCoordinator的心跳线程 和 拉取数据处理数据的处理线程。

heartbeat.interval.ms 心跳间隔时间,或则来了新的消费者 GroupCoordinator 做了reblance 重新分配分区后,通过心跳响应发个 各个消费者的时间。 只要消费者没挂掉,心跳一直在。

session.timeout.ms GroupCoordinator如果超过这个时间 没有收到消费者的消息,就认为消费者挂了,会移出消费者组。 配的值要比heartbeat.interval.ms大。


max.poll.interval.ms 参数用于指定consumer两次poll的最大时间间隔(默认5分钟),如果超过了该间隔consumer client会主动向coordinator发起LeaveGroup请求,触发rebalance;然后consumer重新发送JoinGroup请求。JoinGroup请求就不带失败的那个消费者了。

(1)实际应用中,消费到的数据处理时长不宜超过max.poll.interval.ms,否则会触发rebalance

(2)如果处理消费到的数据耗时,可以尝试通过减小max.poll.records的方式减小单次拉取的记录数(默认是500条)






因为心跳线程和处理线程是两个线程, 只有当整个消费进程阻塞 比如做FullGC 才会导致 消费者发不出心跳导致 session.timeout.ms 超时。

只有当拉取的数据过多,或消费达到处理极限,时间过长 才会导致提交超时 失败,导致被踢出消费分组。做reblance重新分配消费分区。 可能有失败重试机制,导致消费重新加入消费分组,再拉取处理超时,再被提出,一直reblance。

 

 


session.timeout.ms 和 heartbeat.interval.ms的区别详解:

因为一个topic往往有多个分区,而我们又会在一个consumer group里面创建多个消费者消费这个topic,因此:就有了一个问题:哪些的分区的消息交给哪个consumer消费呢?

 

这里涉及到三个概念:consumer group,consumer group里面的consumer,以及每个consumer group有一个 group coordinator。

GroupCoordinator 是负责进行 consumer 的 group 成员与 offset 管理,属于 Kafka Server 端服务,Broker 在启动是都会启动 GroupCoordinator 服务。

 


conusmer分区分配是通过组管理协议来实施的:

consumer group里面的各个consumer都向 group coordinator发送JoinGroup请求,这样group coordinator就有了所有consumer的成员信息,于是它从中选出一个consumer作为Leader consumer,并告诉Leader consumer说:你拿着这些成员信息和我给你的topic分区信息去安排一下哪些consumer负责消费哪些分区吧

接下来,Leader consumer就根据我们配置的分配策略(由参数partition.assignment.strategy指定)为各个consumer计算好了各自待消费的分区。于是,各个consumer向 group coordinator 发送SyncGroup请求,但只有Leader consumer的请求中有分区分配策略,group coordinator 收到leader consumer的分区分配方案后,把该方案下发给各个consumer。

而在正常情况下 ,当有consumer进出consumer group时就会触发rebalance,所谓rebalance就是重新制订一个分区分配方案。而制订好了分区分配方案,就得及时告知各个consumer,这就与 heartbeat.interval.ms参数有关了。具体说来就是:每个consumer 都会根据 heartbeat.interval.ms 参数指定的时间周期性地向group coordinator发送 hearbeat,group coordinator会给各个consumer响应,若发生了 rebalance,各个consumer收到的响应中会包含 REBALANCE_IN_PROGRESS 标识,这样各个consumer就知道已经发生了rebalance,同时 group coordinator也知道了各个consumer的存活情况。

那为什么要把 heartbeat.interval.ms 与 session.timeout.ms 进行对比呢?session.timeout.ms是指:group coordinator检测consumer发生崩溃所需的时间。一个consumer group里面的某个consumer挂掉了,最长需要 session.timeout.ms 秒检测出来。举个示例session.timeout.ms=10,heartbeat.interval.ms=3

session.timeout.ms是个"逻辑"指标,它指定了一个阈值---10秒,在这个阈值内如果coordinator未收到consumer的任何消息,那coordinator就认为consumer挂了。而heartbeat.interval.ms是个"物理"指标,它告诉consumer要每3秒给coordinator发一个心跳包,heartbeat.interval.ms越小,发的心跳包越多,它是会影响发TCP包的数量的,产生了实际的影响,这也是我为什么将之称为"物理"指标的原因。

如果group coordinator在一个heartbeat.interval.ms周期内未收到consumer的心跳,就把该consumer移出group,这有点说不过去。就好像consumer犯了一个小错,就一棍子把它打死了。事实上,有可能网络延时,有可能consumer出现了一次长时间GC,影响了心跳包的到达,说不定下一个heartbeat就正常了。

而heartbeat.interval.ms肯定是要小于session.timeout.ms的,如果consumer group发生了rebalance,通过心跳包里面的REBALANCE_IN_PROGRESS,consumer就能及时知道发生了rebalance,从而更新consumer可消费的分区。而如果超过了session.timeout.ms,group coordinator都认为consumer挂了,那也当然不用把 rebalance信息告诉该consumer了。


在kafka0.10.1之后的版本中,将session.timeout.ms 和 max.poll.interval.ms 解耦了。也就是说:new KafkaConsumer对象后,在while true循环中执行consumer.poll拉取消息这个过程中,其实背后是有2个线程的,即一个kafka consumer实例包含2个线程:一个是heartbeat 线程,另一个是processing线程,processing线程可理解为调用consumer.poll方法执行消息处理逻辑的线程,而heartbeat线程是一个后台线程,对程序员是"隐藏不见"的。如果消息处理逻辑很复杂,比如说需要处理5min,那么 max.poll.interval.ms可设置成比5min大一点的值。而heartbeat 线程则和上面提到的参数 heartbeat.interval.ms有关,heartbeat线程 每隔heartbeat.interval.ms向coordinator发送一个心跳包,证明自己还活着。只要 heartbeat线程 在 session.timeout.ms 时间内 向 coordinator发送过心跳包,那么 group coordinator就认为当前的kafka consumer是活着的。


在kafka0.10.1之前,发送心跳包和消息处理逻辑这2个过程是耦合在一起的,试想:如果一条消息处理时长要5min,而session.timeout.ms=3000ms,那么等 kafka consumer处理完消息,group coordinator早就将consumer 移出group了,因为只有一个线程,在消息处理过程中就无法向group coordinator发送心跳包,超过3000ms未发送心跳包,group coordinator就将该consumer移出group了。而将二者分开,一个processing线程负责执行消息处理逻辑,一个heartbeat线程负责发送心跳包,那么:就算一条消息需要处理5min,只要底heartbeat线程在session.timeout.ms向group coordinator发送了心跳包,那consumer可以继续处理消息,而不用担心被移出group了。另一个好处是:如果consumer出了问题,那么在 session.timeout.ms内就能检测出来,而不用等到 max.poll.interval.ms 时长后才能检测出来。

 

 

一次kafka consumer 不断地 rebalance 分析
明白了session.timeout.ms 和 max.poll.interval.ms 和 heartbeat.interval.ms三个参数的意义后,现在来实际分析一下项目中经常碰到的 consumer rebalance 错误。
一般我们是在一个线程(用户线程)里面执行kafka consumer 的while true循环逻辑的,其实这里有2个线程:一个是用户线程,另一个是心跳线程。心跳线程,我想就是根据heartbeat.interval.ms参数配置的值周期性向coordinator发送心跳包以证明consumer还活着。
如果消息处理逻辑过重,也即用户线程需要执行很长的时间处理消息,然后再提交offset。咋一看,有一个后台心跳线程在不断地发送心跳啊,那为什么group coordinator怎么还老是将consumer移出group,然后导致不断地rebalance呢?
我想,问题应该是 max.poll.interval.ms这个参数引起的吧,因为在ERROR日志中,老是提示:消息处理逻辑花了太长的时间,要么减少max.poll.records值,要么增大session.timeout.ms的值。尽管有后台heartbeat 线程,但是如果consumer的消息处理逻辑时长超过了max.poll.interval.ms ,那么此consumer提交offset就会失败:

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.

此外,在用户线程中,一般会做一些失败的重试处理。比如通过线程池的 ThreadPoolExecutor#afterExecute()方法捕获到异常,再次提交Runnable任务重新订阅kafka topic。本来消费处理需要很长的时间,如果某个consumer处理超时:消息处理逻辑的时长大于max.poll.interval.ms (或者消息处理过程中发生了异常),被coordinator移出了consumer组,这时由于失败的重试处理,自动从线程池中拿出一个新线程作为消费者去订阅topic,那么意味着有新消费者加入group,就会引发 rebalance,而可悲的是:新的消费者还是来不及处理完所有消息,又被移出group。如此循环,就发生了不停地 rebalance 的现象。


参考资料:https://www.cnblogs.com/hapjin/archive/2019/06/01/10926882.html

 session.timeout.ms 和 max.poll.interval.ms 和 heartbeat.interval.ms三个参数总结:

heartbeat.interval.ms (时间配置的要最小),指规定发送心跳的时间,正常就会按这个时间一直发送心跳,即使超过了这个时间没有发送心跳,但时间没超过session.timeout.ms也不会有任何报错信息。

session.timeout.ms   (大于heartbeat.interval.ms  ,小于max.poll.interval.ms  )指超过这个时间,group coordinator 没有收到消费的任何信息,包括心跳和拉取数据的信息都没有收到,就会把消费者踢出去,重新reblance。 是感知消费端出问题的最短时间,如果消费端出了问题,发不出心跳,也没有拉取数据,不用等到max.poll.interval.ms 时间,只要超过session.timeout.ms 时间,服务端就会感知到,提示报错,并触发reblance。

max.poll.interval.ms  (配置的超过session.timeout.ms) ,允许消费者处理的最长时间,可以适当允许长一点,只要一直有心跳,处理时间没有超过这个值,就一切正常。

 

 

二、设置kafka的数据过期时间
修改kafka服务端的server.properties配置
log.retention.hours=72
log.cleanup.policy=delete

组与组间的消费者是没有关系的。
topic中已有分组消费数据,新建其他分组ID的消费者时,之前分组提交的offset对新建的分组消费不起作用。

 

四、Flink Kafka consumer的消费策略配置(可以指定消费起始位置)
FlinkKafkaConsumer011[String] consumer = new FlinkKafkaConsumer011[String]("hello", valueDeserializer, kafkaProps)
// 指定消费策略
consumer.setStartFromEarliest() // - 从最早的记录开始;
consumer.setStartFromLatest() //- 从最新记录开始;
consumer.setStartFromTimestamp(null); // 从指定的epoch时间戳(毫秒)开始;
consumer.setStartFromGroupOffsets(); // 默认行为,从上次消费的偏移量进行继续消费。

posted on 2019-12-19 10:44  luckyna  阅读(2074)  评论(0编辑  收藏  举报

导航