基于rocketmq-4.9.0 版本分析rocketmq
1.什么是消费偏移量offset?
我们先看一幅图

消费偏移量offset就是记录消费者的消费进度的。也是rocketmq保证消息不会重复消费的核心(当然,极端情况下还是可能会导致重复消费)。
consumequeue中一个消息的索引单元就是一个offset值。
在分析rocketmq的消费者是如何利用这个offset完成消息消费的之前,我们先看下broker端是如何管理这些offset值的。
服务端管理offset
这里的服务端就是broker
- broker在初始化(
initialize())时会通过消费者offset管理类ConsumerOffsetManager来加载配置文件中的offset值,然后设置到offsetTable属性中。
public class ConsumerOffsetManager extends ConfigManager {
//TODO: key = topic@group
//TODO: value = Map[key = queueId, value = offset]
private ConcurrentMap<String/* topic@group */, ConcurrentMap<Integer, Long>> offsetTable =
new ConcurrentHashMap<String, ConcurrentMap<Integer, Long>>(512);
//TOOD:...other
}
offset文件默认路径:$user.home/store/config/consumerOffset.json 文件内容例子:
{
"offsetTable":{
//topic@consumerGroupName
"batchTopic@test_cg_batch":{0:0,1:0,2:0,3:1
},
//key是queueid,value是offset值,就是我们今天讨论的主角
"ordered_topic@CG":{0:0,1:15,2:0,3:35
},
"qiuguan_topic@qiuguan_group_1":{0:2533,1:2534,2:2531,3:2531
},
"hacker_topic@fuyuanhui_group_2":{0:64035,1:64034,2:64034,3:64034
},
"qiuguan_topic_2@qiuguan_group":{0:2,1:1,2:7,3:6
},
"qiuguan_topic@qiuguan_group":{0:2533,1:2534,2:2531,3:2531
}
}
}
- 消费者消费后,会将offset发送到broker,这里会先写入到上面的消费者offset管理类
ConsumerOffsetManager的offsetTable中,然后通过定时任务将其刷盘到文件中。属性中。然后通过3将数据持久化到文件中
稍后我们分析消费者时还会看到
- broker在初始化(
initialize())时,会启动定时任务,每隔5秒执行一次初始化,将ConsumerOffsetManager的offsetTable属性中的值持久化到文件中。
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.consumerOffsetManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumerOffset error.", e);
}
}
}, 1000 * 10, 1000 * 5, TimeUnit.MILLISECONDS);
那么服务端对于offset值的管理大致就这些,那么我们来看下消费者是如何利用offset来进行消息消费的。
总的来说就是,消费者定时的将消费过的offset值上传到broker的内存
offsetTable中,然后通过定时任务将其刷盘到文件中。
那么接下来就看看消费者是如何使用这个offset值的。
3.消费者使用offset
3.1 消费者初始化offset
前面在分析consumer消费时我们知道,它会启动一个消息拉取服务PullMessageService对象,还有一个是在拉取消息之前要完成的重平衡RebalanceService对象。offset初始化就和重平衡息息相关,那么我们就看下重平衡是如何完成offset初始化的。
我们这里还是只讨论集群消费模式。它和广播模式的区别就是,广播模式每个消费者都要消费topic下的所有队列,集群模式通过分配算法(默认是平均)来将topic下的所有队列分配给消费者。既然这里我们主要讨论的是offset,那么就以集群模式进行分析即可。
前面我们分析了重平衡,那么这里我们就只看和offset初始化相关的部分
RebalanceService#run()一步一步来到初始化offset的地方
private boolean updateProcessQueueTableInRebalance(final String topic, final Set<MessageQueue> mqSet,
final boolean isOrder) {
boolean changed = false;
//TODO: 省略部分代码........
List<PullRequest> pullRequestList = new ArrayList<PullRequest>();
for (MessageQueue mq : mqSet) {
if (!this.processQueueTable.containsKey(mq)) {
if (isOrder && !this.lock(mq)) {
log.warn("doRebalance, {}, add a new mq failed, {}, because lock failed", consumerGroup, mq);
continue;
}
this.removeDirtyOffset(mq);
ProcessQueue pq = new ProcessQueue();
long nextOffset = -1L;
try {
//TODO:初始化偏移量
nextOffset = this.computePullFromWhereWithException(mq);
} catch (MQClientException e) {
log.info("doRebalance, {}, compute offset failed, {}", consumerGroup, mq);
continue;
}
if (nextOffset >= 0) {
ProcessQueue pre = this.processQueueTable.putIfAbsent(mq, pq);
if (pre != null) {
log.info("doRebalance, {}, mq already exists, {}", consumerGroup, mq);
} else {

本文详细解析了RocketMQ中消费偏移量offset的管理机制,包括服务端的offset存储、消费者如何初始化和使用offset,以及offset的更新和持久化过程。核心内容涉及offset表的结构、消费模式下的offset计算和同步策略,以及offset的持久化策略。
最低0.47元/天 解锁文章

3321

被折叠的 条评论
为什么被折叠?



