下文以DefaultMQPushConsumerImpl集群模式消费消息为例。
概述
消息消费完成后,需要将消费进度存储起来,即前面提到的offset。广播模式下,同消费组的消费者相互独立,消费进度要单独存储;集群模式下,同一条消息只会被同一个消费组消费一次,消费进度会参与到负载均衡中,故消费进度是需要共享的。
消费者端
提交offset入口
入口在org.apache.rocketmq.client.impl.consumer.ConsumeMessageConcurrentlyService#processConsumeResult中的最后一段逻辑:
public void processConsumeResult(
final ConsumeConcurrentlyStatus status,
final ConsumeConcurrentlyContext context,
final ConsumeRequest consumeRequest
) {
------------------省略-----------------------
long offset = consumeRequest.getProcessQueue().removeMessage(consumeRequest.getMsgs());
if (offset >= 0 && !consumeRequest.getProcessQueue().isDropped()) {
this.defaultMQPushConsumerImpl.getOffsetStore().updateOffset(consumeRequest.getMessageQueue(), offset, true);
}
}
消息消费完成(不论成功或失败)后,将消息从ProcessQueue中移除,同时返回ProcessQueue中最小的offset,使用这个offset值更新消费进度,removeMessage返回的offset有两种情况,一是已经没有消息了,返回
ProcessQueue最大offset+1,二是还有消息,则返回未消费消息的最小offset。举个例子,ProcessQueue中有offset为101-110的10条消息,如果全部消费完了,返回的offset为111;如果101未消费完成,102-110消费完成,则返回的offset为101,这种情况下如果消费者异常退出,会出现重复消费的风险,所以要求消费逻辑幂等。
updateOffset逻辑
看RemoteBrokerOffsetStore的updateOffset()逻辑,将offset更新到内存中,这里RemoteBrokerOffsetStore使用ConcurrentHashMap保存MessageQueue的消费进度:
@Override
public void updateOffset(MessageQueue mq, long offset, boolean increaseOnly) {
if (mq != null) {
AtomicLong offsetOld = this.offsetTable.get(mq);
if (null =&#