上篇讲到消息从broker拉取后,会把消息存入PullRequest的processQueue中。此时就会有其他线程,对这些消息进行消费。

其他线程包括顺序消费和并发消费。

并发消费

并发消费中,用于消费消息的线程叫做ConsumeRequest,每一个ConsumeRequest默认消费32条消息,如果需要消费的消息超过32条,就会创建多个ConsumeRequest。这些ConsumeRequest就会放入线程池中等待运行。

image.png

ConsumeRequest线程运行的时候,会调用监听器,拿到消息消费的结果,并发消费的结果有两种,RECONSUME_LATER和CONSUME_SUCCESS。

image.png

RECONSUME_LATER说明需要重新消费,但广播模式下只打印警告级别的日志,集群模式下,重新封装消息成ConsumeRequest,再放入线程池,5s后再进行消费。

image.png

同时,也需要跟broker说,这个消息消费失败了。broker创建重试主题,并设置消息重试次数,当消息拉取超过重试次数后,就会进入DLQ队列中。

image.png

有哪些消息需要重新消费?比如这次消息有10个,ackIndex是2,那下标为3到9的消息,都要重新消费。如果这个结果是CONSUME_LATER,ackIndex就会被设置为-1,那就是所有的消息,都要重新消费。

NSUME_SUCCESS说明消费成功,根据ackIndex计算一下成功和失败的信息。

对于RECONSUME_LATER或NSUME_SUCCESS的消息处理完后,就要把消息从ProcessQueue中移除,并更新OffsetStore里的消息消费进度。

OffsetStore里的消息消费进度会由一个定时任务,每5s会提交给broker,然后broker再更新偏移量。所以即便消息消费成功了,但是还没提交给broker的时候,宕机了,那下次还是从之前的偏移量开始读取数据,导致消息重复消费。

顺序消费

顺序消费中,也是封装一个ConsumeRequest,这个ConsumeRequest和并发消费的ConsumeRequest不同,他并没有消息列表,所以消息直接从ProcessQueue拿。

image.png

线程消费的时候,首先会获取一个锁。这个锁的粒度是消息队列,也就是说,一个消息消费队列,只能有一个线程进行消费。

image.png

获取锁后,就从ProcessQueue把消息拿出来。

image.png

如果ProcessQueue有消息,那还需要拿到ProcessQueue的ConsumeLock。

image.png

然后进行消息消费,并通过监听器拿到消费结果的状态,并释放锁。

image.png

如果结果是SUCCESS,则更新ProcessQueue中的消息消费信息。

如果结果是SUSPEND_CURRENT_QUEUE_A_MOMENT,则会检查消息的重试次数。

如果这个重试次数超过允许的最大重试次数,就会把这个消息发送给broker,且存放在死信队列中。

如果没有超过,则会继续重新消费,并累加消费次数。

最后,再更新OffsetStore里的消息消费进度。

OffsetStore的处理,和并发消费一样。

线程拿到messageQueueLock锁后,并不会一直持有这个锁,默认消费60s,就会释放这个锁。


大军
847 声望183 粉丝

学而不思则罔,思而不学则殆