4.4.2 将拉取偏移量作为提交偏移量


4.4.2 将拉取偏移量作为提交偏移量

旧API中,当客户端迭代消费消息时会更新分区信息的已消费偏移量,并且有一个后台线程定时将分区信息的已消费偏移量作为已提交偏移量发送给协调者节点。

新API中,订阅状态的分区状态有拉取偏移量(posi.ti.on)和提交偏移量(COmmitted)两个变量。客户端的轮询方法会在返回拉取的记录集之前,更新分区状态的拉取偏移量,为下一次轮询操作中的拉取做准备。但客户端在迭代消费者记录集时,并没有更新分区状态的提交偏移量。所以拉取偏移量变量也要能够代表分区的消费进度,即新API会使用拉取偏移量的值作为分区的提交偏移量发送给协调者节点。相关代码如下:
在这里插入图片描述

新API在迭代消息时没有更新订阅状态的任何变量,可以认为并不存在且消费偏移量这个变量。分区状态还要保存提交偏移量这个变量的原因是:在轮询时,如果分区没有拉取偏移量,需要从协调者获取其他消费者提交的分区偏移量,然后保存到分区状态对象的提交偏移量,再将提交偏移量赋值给拉取偏移量,这样分区状态的拉取偏移量就有数据了,客户端才可以发送拉取请求拉取消息。

问题是:新API的定时提交任务为什么可以直接使用拉取偏移量作为已提交偏移量?实际上消费者向动提交任务提交偏移量和轮询操作也有关系。下面先来回顾一下消费者网络客户端轮询时弹出超时任务的调用步骤。客户端轮询时间(cHentPoll()方法的t"i.meO川参数)取的是两个时间的最小值:客户端的超时时间(poll()方法的ti.meO川参数)、调度时间与当前时间之差(最大为0)。公式为:客户端轮询时间=min(客户端的超时时间,max(调度时间-当前时间,0))。相关代码如下:

在这里插入图片描述

如果当前时间比延迟任务的调度时间大或相等,即调度时间一当前时间<=O,则说明延迟任务已经超时,应该立即调度,这时候:

客户端轮询时间=min(客户端的超时时间,max(调度时间-当前时间,0))=min(客户端的
超时时间,max(<=O,0))=min(客户梢的超时时间,。)=O

即客户端轮询的超时时间为0,等价于快速轮询。如图4-37所示,假设客户端的超时时间为2秒,有一个延迟任务的调度时间是4秒。对于不同的当前时间,客户端轮询的超时时间也不同。一旦当前时间超过或等于调度时间,最后的结果一定是0,说明延迟任务一旦超时,在客户端轮询时,会被马上发现,并被立即调度。

(1)当前时间=I秒:客户端轮询时间=rri.n(Z,max(4-1,0))=1'11.n(Z,3)=2秒。
(2)当前时间=2秒:客户端轮询时间=1'11.n(2,max(4-2,0))=1'11.n(2,2)=2秒。
(3)当前时间=3秒:客户端轮询时间=1'11.n(2,max(4-3,0))=1'11.n(2,1)=I秒。
(4)当前时间=4秒:客户端轮询时间=min(2,max(4-4,0))=min(2,。)=0秒。
(5)当前时间=5秒:客户端轮询时间=min(2,max(4-5,0))=1'11.n(2,。)=0秒。

在这里插入图片描述

因为延迟任务的调度是在客户端的轮询中触发,而客户端的轮询又是在Kafka消费者的轮询方法中调用的,所以如果Kafka消费者没有轮询,就不会执行延迟的任务。即使任务超时了,它也没有机会从延迟队列中移除出去并执行。

Kafka的轮询除了客户端的轮询(在客户端轮询之前,还有发送拉取请求),还有一个步骤是拉取器获取记录集,客户端应用程序调用一次Kafka的轮询,会返回一批消费者记录集。拉取器在返回获取的记录集给客户端应用程序处理之前,会更新本次拉取记录集后的订阅状态,即分区的拉取偏移量。

综合上面两点的背景知识,再结合拉取器拉取消息、Kafka轮询的流程,具体步骤如下。

(1)拉取器发送拉取请求;客户端轮询,会把拉取请求发送出去。
(2)客户端轮询还有可能弹出超时的延迟任务,比如定时提交任务的调度时间到了,应该立即执行。
(3)拉取器的拉取请求完成后,通过回调处理器暂存拉取结果。
(4)拉取器调用获取记录集方法,更新订阅状态中分区的拉取偏移量,并返回结果给客户端应用程序。
(5)最后客户端应用程序开始处理Kafka轮询返回的消费者记录集。

相关代码如下:
在这里插入图片描述

从上面的步骤中可以得出的结论是:延迟的提交任务超时后会被立即执行,它会比获取记录集时更新分区状态的拉取偏移量要早。Kafka轮询到结果集后,前面这两个步骤都执行完后,客户端应用程序才会真正处理拉取的消费者记录集。在引人前面提到的问题之前,先引出一个新的问题。

拉取器返回拉取到的消费者记录集之前,会更新分区的拉取偏移址,然后客户端应用程序才处理这批消费者记录集。那么会不会出现一种异常情况:消费者处理这批记录集失败了,但是定时提交任务会提交更新过的拉取偏移量?比如拉取器拉取到的分区数据是[4,5,呵,并将分区的拉取偏移盐更新为6,但客户端应用程序还没有开始处理,定时提交任务会不会将6作为提交偏移蓝?实际上这种情况不会发生,因为定时提交任务比更新偏移量、处理消费者记录集都要早,定时提交任务获取拉取偏移革时,拉取器一定还没有更新分区的拉取偏移量。以前面的示例来说,它获取的分区拉取偏移盘一定不会是6,只能是4之前的3,把3作为分区的提交偏移量。所以并不会存在消费者没有处理消息,但定时提交任务却提交了消息的情况。

如图4-38所示,以延迟任务的调度、拉取器获取记录集、更新拉取偏移量、消费者处理记录集4个步骤的执行过程来说明,定时提交任务可以采用拉取偏移量作为提交偏移盘的原因。假设客户端轮询时间为l秒,定时提交任务间隔为5秒,下面详细说明了消费者5次轮询的执行过程,主要看第五次轮询。

在这里插入图片描述

(1)消费者第一次轮询时,延迟任务没有超时不会执行,拉取器获取记录集[1,2),更新拉取偏移量为2,消费者处理记录集[1,2)。
(2)第二次轮询时延迟任务没有超时不会执行,拉取器获取记录集[3,4),更新拉取偏移量为4,消费者处理记录集[3,4)。
(3)第三次轮询时延迟任务没有超时不会执行,没有拉取到记录集。
(4)第四次轮询时延迟任务没有超时不会执行,拉取器获取记录集[5,6],更新拉取偏移量为6,消费者处理记录集[5,6)。
(5)第五次轮询时延迟任务坦时,定时提交任务开始执行,它要获取的最新分区拉取偏移量,来向于步骤(4)更新后的值,等于6。因为定时提交任务是异步提交模式,所以会立即返回到主流程。接着拉取器获取记录集[7,8,9],更新拉取偏移量为9,消费者处理记录集[7,8,9]。

上面流程的第五次轮询在执行定时提交任务时,因为这个时候拉取器还没有拉取到新消息,或者即使拉取到了新消息,没有调用获取记录集的方法,也不会更新拉取偏移量。所以这时定时提交任务会将分区拉取偏移fil值(6)作为分区的最近消费进度提交到协调者。我们必须要保证消费者“提交偏移量”这个位置的消息被客户端应用程序消费过,才不会丢失数据。而实际上,消息6也确实已经在步骤(4)被客户端应用程序消费完成了。

现在来回答“定时提交任务为什么可以采用拉取偏移量作为提交偏移茧”了。定时提交任务在超时后会立即执行,并且发生在本次轮询中拉取器更新最新一批记录集的拉取偏移量之前。而且这一次Kafka轮询中的定时提交任务一定发生在上一次的Kafka轮询都全部执行完成之后,而上一次Kafka轮询一定成功更新了拉取偏移茧,井且也成功处理了上一次拉取的那批记录集。所以本次轮询中定时提交任务需要获取的提交偏移量,实际上等价于上一次轮询更新后的拉取偏移量。

消费者拉取消息、心跳请求以及本节的定时提交任务都和轮询有关。可见,轮询是消费者的人口,通过轮询,只要事件发生,就有对应的处理逻辑来接手,后端的操作对于消费者都是透明的。


相关推荐
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页