前言
大家好哇,今天跟大家一起了解下RocketMq的offset管理机制~
什么是offSet?
一个Topic会存在多个messageQueue,每个messageQueue都会被Consumer消费,那么Consumer该从哪开始消费呢?
这就涉及到offset了,每个messageQueue会记录一个offset表示当前的消费进度,Consumer直接从offset开始消费即可。
借用一下网上的图~
Broker管理Offset
持久化
broker在启动时,会开启定时任务,每隔5秒将ConsumerOffsetManager中的offsetTable持久化到consumerOffset.json文件中
java复制代码public boolean initialize() throws CloneNotSupportedException {
// .....
this.scheduledExecutorService.scheduleAtFixedRate(() -> {
try {
// 持久化
BrokerController.this.consumerOffsetManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumerOffset error.", e);
}
}, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
// .....
}
那么offsetTable是个什么东西?为什么要专门将它进行持久化呢?
java复制代码public class ConsumerOffsetManager extends ConfigManager {
protected ConcurrentMap<String/* topic@group */, ConcurrentMap<Integer, Long>> offsetTable =
new ConcurrentHashMap<String, ConcurrentMap<Integer, Long>>(512);
}
进入到ConsumerOffsetManager中,我们可以看到offsetTable本质是一个map结构,key为topic@group,value为一个key是队列下标、value为消费偏移量offset的map结构
具体的持久化文件结构如下👇🏻
json复制代码{
"offsetTable":{
"TopicTest@please_rename_unique_group_name_4":{0:4,1:4,2:4,3:4,4:4,5:2,6:2,7:1,8:3,9:1,10:0,11:2,12:2,13:3,14:3,15:3
},
"%RETRY%please_rename_unique_group_name_4@please_rename_unique_group_name_4":{0:0
},
"TopicTest-2@please_rename_unique_group_name_4":{0:25,1:25,2:25,3:25,4:25,5:25,6:25,7:25
},
"TopicTest-1@please_rename_unique_group_name_4":{0:12,1:12,2:12,3:12,4:13,5:13,6:13,7:13
}
}
}
offset文件默认存储路径:$user.home/store/config/consumerOffset.json
更新
上面我们了解了broker对于offsetTable的持久化,下面我们继续来了解offsetTable是如何被更新的
当消费者消费消息后,会将offset发送给broker,broker接收后会更新offsetTable,接着交由定时任务进行持久化
而broker则是通过ConsumerManageProcessor来处理更新offset请求的
java复制代码public class ConsumerManageProcessor extends AsyncNettyRequestProcessor {
@Override
public RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request)
throws RemotingCommandException {
switch (request.getCode()) {
case RequestCode.GET_CONSUMER_LIST_BY_GROUP:
return this.getConsumerListByGroup(ctx, request);
case RequestCode.UPDATE_CONSUMER_OFFSET:
// todo 更新consumer offset
return this.updateConsumerOffset(ctx, request);
case RequestCode.QUERY_CONSUMER_OFFSET:
// todo 查询consumer消费最新offset
return this.queryConsumerOffset(ctx, request);
default:
break;
}
return null;
}
}
updateConsumerOffset中,从requestHeader中拿到consumerGroup、topic、queueId、offset等信息,调用ConsumerOffsetManager#commitOffset来更新offsetTable
java复制代码private RemotingCommand updateConsumerOffset(ChannelHandlerContext ctx, RemotingCommand request)
throws RemotingCommandException {
final RemotingCommand response =
RemotingCommand.createResponseCommand(UpdateConsumerOffsetResponseHeader.class);
final UpdateConsumerOffsetRequestHeader requestHeader =
(UpdateConsumerOffsetRequestHeader) request
.decodeCommandCustomHeader(UpdateConsumerOffsetReq