如何存储队列位置信息
结合具体的应用场景,难免会遇到重新消费某条消息,比如返回消息消费失败,让MQ重新投递,或者需要跳过某一段时间内的消息进行消费等情况,为了解决这些需求,都需要将队列位置信息offset进行存储。因此本文将分析offset的存储位置,以及如何调整offset的值。
- 何为offset
在RocketMQ中,一种类型的消息会拥有一个Topic,为了提高并行消费能力,一个Topic会有多个Message Queue,那么offset就是指在某个Topic下的一条消息在某个Message Queue里的位置信息,通过offset就可以定位到这个消息本身的索引值,进而可以指示Consumer从某条消息后开始消费。
- offset的设计
对于DefaultMQPushConsumer来说,默认是MessageModel.CLUSTERING,集群模式,也就是同一个Consumer Group中的多个消费者每个消费一部分,各自消费的消息是不同的,它是由Broker端存储、控制offset的值。使用的RemoteBrokerOffsetStore结构;在MessageModel.BROADCASTING,广播模式下,消费组的每个消费者都能消费这个Topic的全部消息,每个消费者之间相对独立,使用的LocalFileOffsetStore结构,把offset存到本地。(offsetStore使用JSON格式进行存储)
使用DefaultMQPushConsumer的时候,采用PULL模式,消费者不用关心offsetStore,只需要处理MQ给consumer推送的消息即可,但是如果使用DefaultMQPullConsumer,就需要自己处理offset,选择从哪里开始消费。
consumer.start();
Set<MessageQueue> queues = consumer.fetchSubscribeMessageQueues("Topic1");
for(MessageQueue queue:queues){
//long offset = consumer.fetchConsumeOffset(queue,true);//得到每一个相关联Queue起始的offset
//System.out.println(queue.getQueueId()+" "+queue.getTopic()+" "+queue.getBrokerName()+"=> "+offset);
for(;;){
long _offset = map.get(queue)==null?0:map.get(queue);//得到queue后从map中去取
PullResult pullResult = consumer.pullBlockIfNotFound(queue, null, _offset, MAX_NUM);
//把下一次要读取的offset进行存储
System.out.println("minOffset "+pullResult.getMinOffset()+" maxOffset "+pullResult.getMaxOffset());
long nextOffset = pullResult.getNextBeginOffset();
System.out.println("nextBeginOffset "+nextOffset);
map.put(queue,nextOffset);
List<MessageExt> msgList = pullResult.getMsgFoundList();
if(null!=msgList){
for(MessageExt l:msgList){
System.out.println(l.getMsgId()+