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;
			}
		}
	}
}
發佈了24 篇原創文章 · 獲贊 11 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章