RocketMQ-消息存储(四)、消息消费队列和索引文件构建机制

当消息生产者提交的消息存储在CommitLog文件中时,会及时更新ConsumeQueue、IndexFile。RocketMQ通过开启一个线程ReputMessageService来准实时准发CommitLog文件更新事件,相应的任务处理器根据转发的消息及时更新consumeQueue、IndexFile文件。

消息消费转发的核心实现在StoreMessage的doReput方法中实现:
1、返回 reputFromOffset 偏移量开始的全部有效数据(commitlog 文件)。然后循环 读取每一条消息。
2、从result 返回的 ByteBuffer 中循环读取消息,一次读取一条,创建 DispatchRequest对象( 在DispatchRequest中,如果消息长度大于 ,则调用 doDispatch 方法)。最终将分别调用 CommitLogDispatcherBuildConsumeQueue (构建消息消费队 )、 CommitLogDispatcherBuildlndex (构建索引文件)

根据消息更新ConsumeQueue:
1)、根据消息主题与队列 ID ,先获取对应的 ConumeQueue文件,其逻辑比较简单, 因为每一个消息主题对应一个消息消费队列目录, 然后主题下每一个消息队列对应一个文件夹,然后取出该文件夹最后的ConsumeQueue文件即可

public void putMessagePositionInfo(DispatchRequest dispatchRequest){
	ConsumeQueue cq = this.findConsumeQueue
	(dispatchRequest.getTopic(),dispatchRequest.getQueueId());
	cq.putMessagePositionInfoWrapper(dispatchRequest);
}

2)、依次将消息偏移量、消息长度、tag hashcode写入到ByteBuffer中,并根据consumeQueueOffset计算ConsumeQueue中的物理地址,将内容追加到ConsumeQueue的内存映射内存中(本操作只追击不刷盘),ConsumeQueue的刷盘方式固定为异步刷盘模式。

this.byteBufferIndex.flip();
this.byteBufferIndex.limit(CQ_STORE_UNIT_SIZE);
this.byteBufferIndex.putLong(offset);
this.byteBufferIndex.putInt(size);
this.byteBufferIndex.putLong(tagsCode);

final long expectLogicOffset = cqOffset * CQ_STORE_UNIT_SIZE;

MappedFile mappedFile =  this.mappedFileQueue.getLastMappedFile
					(expectLogicOffset);
if(mappedFile!=null){
	Return mappedFile.appendMessage(this.byteBufferIndex.array());
}

根据消息更新Index索引文件:
Hash索引文件转发任务实现类是CommitDispatcherBuildIndex,首先判断isMessageIndexEnable设置为true,则调用IndexService的buildIndex方法构建Hash索引,否则忽略本次转发任务。
1、获取或创建 IndexFile 文件并获取所有文件最大的物理偏移量。如果该消息的物 理偏移量小于索引文件中的物理偏移,则说明是重复数据,忽略本次索引构建

IndexFile indexFile = retryGetAndCreateIndexFile();
if(indexFile != null){
	Long endPhyOffset = indexFile.getEndPhyOffset();
	DispatchRequestmsg = req;
	Stringtopic = msg.getTopic();
	Stringkeys = msg.getKeys();
	if(msg.getCommitLogOffset() < endPhyOffset){
		return;
	}
}

2、如果消息的唯一键不为空,则添加到Hash索引中,以便加速根据唯一键检索消息

if(req.getUniqKey() != null){
	indexFile = putKey(indexFile,msg,buildKey(topic,req.getUniqKey()));
	if(indexFile == null){
		log.error("putKeyerrorcommitlog{}uniqkey{}",req.getCommitLogOffset(),req.getUniqKey());
		return;
	}
}

3、构建索引键,RocketMQ支持为同一个消息建立多个索引,多个索引键空格分开

if(keys!=null&&keys.length()>0){
	String[]keyset=keys.split(MessageConst.KEY_SEPARATOR);
	for(inti=0;i<keyset.length;i++){
		Stringkey=keyset[i];
		if(key.length()>0){
			indexFile=putKey(indexFile,msg,buildKey(topic,key));
			if(indexFile==null){
				log.error("putKeyerrorcommitlog{}uniqkey{}",
				req.getCommitLogOffset(),req.getUniqKey());
				return;
			}
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值