不管是顺序消费还是并发消费,在一批消息消费完成后都会将消息从processQueue中移除,返回最小processQueue的最小偏移量,并更新消费进度。
广播模式下,消费进度文件存储在各个消费端本地,因为多个消费端是同时消费的,且互相独立的。
集群模式下,多个消费端共同消费同一个队列,且同一时间只有一个消费端在消费,所以消费进度文件是存储在broker的。
消费进度管理接口是:OffsetStore
广播模式下,实现类是LocalFileOffsetStore
集群模式下,实现类是RemoteBrokerOffsetStore
这里主要看下集群模式下的进度管理,消费消费完后的逻辑为
ConsumeMessageConcurrentlyService.processConsumeResult方法:
。。。
long offset = consumeRequest.getProcessQueue().removeMessage(consumeRequest.getMsgs());
if (offset >= 0 && !consumeRequest.getProcessQueue().isDropped()) {
this.defaultMQPushConsumerImpl.getOffsetStore().updateOffset(consumeRequest.getMessageQueue(), offset, true);
}
调用updateOffset方法更新消息进度
RemoteBrokerOffsetStore.updateOffset方法:
public void updateOffset(MessageQueue mq, long offset, boolean increaseOnly) {
if (mq != null) {
AtomicLong offsetOld = this.offsetTable.get(mq);
if (null == offsetOld) {
offsetOld = this.offsetTable.putIfAbsent(mq, new AtomicLong(offset));
}
if (null != offsetOld) {
if (increaseOnly) {
MixAll.compareAndIncreaseOnly(offsetOld, offset);
} else {
offsetOld.set(offset);
}
}
}
}
offsetTable是一个ConcurrentMap<MessageQueue, AtomicLong>,记录每个队列的进度,这里只是更新进度到内存中。
同步进度到broker在persistAll方法中处理
RemoteBrokerOffsetStore.persistAll方法:
public void persistAll(Set<MessageQueue> mqs) {
if (null == mqs || mqs.isEmpty())
return;
final HashSet<MessageQueue> unusedMQ = new HashSet<MessageQueue>();
if (!mqs.isEmpty()) {
for (Map.Entry<MessageQueue, AtomicLong> entry : this.offsetTable.entrySet()) {
MessageQueue mq = entry.getKey();
AtomicLong offset = entry.getValue();
if (offset != null) {
if (mqs.contains(mq)) {
try {
this.updateConsumeOffsetToBroker(mq, offset.get());
log.info("[persistAll] Group: {} ClientId: {} updateConsumeOffsetToBroker {} {}",
this.groupName,
this.mQClientFactory.getClientId(),
mq,
offset.get());
} catch (Exception e) {
log.error("updateConsumeOffsetToBroker exception, " + mq.toString(), e);
}
} else {
unusedMQ.add(mq);
}
}
}
}
if (!unusedMQ.isEmpty()) {
for (MessageQueue mq : unusedMQ) {
this.offsetTable.remove(mq);
log.info("remove unused mq, {}, {}", mq, this.groupName);
}
}
}
很简单就是一个循环遍历所有队列,然后同步到broker中。
那么什么时候调用这个方法同步到broker呢?
MQClientInstance启动时候会启动一些调度任务其中就包括定期同步进度,每10s定时同步所有队列的消费进度到broker中