个人研读的rocketmq源码注释,由于后续的需要,所以研究rocketmq的文件存储原理,觉得有用的双击
核心入口代码DefaultMessageStore
public boolean load() {
boolean result = true;
try {
//判断临时文件是否存在,如果存在则说明系统是异常宕机 lastExitOK=false
// 这个临时文件启动的时候会创建createTempFile 销毁的时候会删除。如果没有正常关闭就不会执行shutdown方法。则认为是异常关闭
boolean lastExitOK = !this.isTempFileExist();
log.info("last shutdown {}", lastExitOK ? "normally" : "abnormally");
//这个是延时队列的操作 ,不看
if (null != scheduleMessageService) {
result = result && this.scheduleMessageService.load();
}
// load Commit Log //把所有的日志文件加载到文件内存队列中
result = result && this.commitLog.load();
// load Consume Queue //把所有的队列文件加载到文件内存队列中
result = result && this.loadConsumeQueue();
if (result) {
//暂时不管
this.storeCheckpoint =
new StoreCheckpoint(StorePathConfigHelper.getStoreCheckpoint(this.messageStoreConfig.getStorePathRootDir()));
//索引文件 根据key查找消息。暂时不管
this.indexService.load(lastExitOK);
this.recover(lastExitOK);
log.info("load over, and the max phy offset = {}", this.getMaxPhyOffset());
}
} catch (Exception e) {
log.error("load exception", e);
result = false;
}
if (!result) {
this.allocateMappedFileService.shutdown();
}
return result;
}
commitLog.load() 和loadConsumeQueue比较简单可以自己读
public boolean load() {
//把所有的文件加载到文件内存队列中
boolean result = this.mappedFileQueue.load();
log.info("load commit log " + (result ? "OK" : "Failed"));
return result;
}
recover主要恢复方法
private void recover(final boolean lastExitOK) {
//队列是commitLog的索引文件 返回值是所有consumeQueue文件的最大偏移量
long maxPhyOffsetOfConsumeQueue = this.recoverConsumeQueue();
//commitlog存储的是真实消息 恢复commitLog 是rocketmq的核心消息存储数据。核心流程和功能点
if (lastExitOK) {
// 没有临时文件,是正常退出
this.commitLog.recoverNormally(maxPhyOffsetOfConsumeQueue);
} else {
//异常关机退出
this.commitLog.recoverAbnormally(maxPhyOffsetOfConsumeQueue);
}
this.recoverTopicQueueTable();
}
recoverConsumeQueue
private long recoverConsumeQueue() {
long maxPhysicOffset = -1;
for (ConcurrentMap<Integer, ConsumeQueue> maps : this.consumeQueueTable.values()) {
for (ConsumeQueue logic : maps.values()) {
//调用恢复方法 获取这个队列中所有的文件mappedFiles进行恢复
logic.recover();
if (logic.getMaxPhysicOffset() > maxPhysicOffset) {
maxPhysicOffset = logic.getMaxPhysicOffset();
}
}
}
//返回所有队列中最大的偏移量
return maxPhysicOffset;
}
logic.recover();
public void recover() {
//获取队列中所有文件
final List<MappedFile> mappedFiles = this.mappedFileQueue.getMappedFiles();
if (!mappedFiles.isEmpty()) {
//从前三个文件开始找。可能是为了预热
int index = mappedFiles.size() - 3;
if (index < 0)
index = 0;
//文件的大小 应该是1024MB
int mappedFileSizeLogics = this.mappedFileSize;
MappedFile mappedFile = mappedFiles.get(index);
ByteBuffer byteBuffer = mappedFile.sliceByteBuffer();
long processOffset = mappedFile.getFileFromOffset();
//临时维护读取的文件大小的偏移量
long mappedFileOffset = 0;
long maxExtAddr = 1;
while (true) {
for (int i = 0; i < mappedFileSizeLogics; i += CQ_STORE_UNIT_SIZE) {
//每次读取20字节的数据,并且向后移动20字节。consumeQueue数据结构
long offset = byteBuffer.getLong();
int size = byteBuffer.getInt();
long tagsCode = byteBuffer.getLong();
//如果读取出来有值。则临时偏移量增加20. 并且真实偏移量和size进行记录
if (offset >= 0 && size > 0) {
mappedFileOffset = i + CQ_STORE_UNIT_SIZE;
//真实偏移量和size进行追加
this.maxPhysicOffset = offset + size;
if (isExtAddr(tagsCode)) {
maxExtAddr = tagsCode;
}
} else {
//false说明文件读到一半没数据了,这个文件没写完
log.info("recover current consume queue file over, " + mappedFile.getFileName() + " "
+ offset + " " + size + " " + tagsCode);
break;
}
}
//读取的临时偏移量size到了文件的最大size。说明这个文件读完了 否则停止循环
if (mappedFileOffset == mappedFileSizeLogics) {
//如果还有下一个文件。设置下一个文件的数据继续读
index++;
if (index >= mappedFiles.size()) {
log.info("recover last consume queue file over, last mapped file "
+ mappedFile.getFileName());
break;
} else {
mappedFile = mappedFiles.get(index);
byteBuffer = mappedFile.sliceByteBuffer();
processOffset = mappedFile.getFileFromOffset();
mappedFileOffset = 0;
log.info("recover next consume queue file, " + mappedFile.getFileName());
}
} else {
// 停止循环打印日志
log.info("recover current consume queue queue over " + mappedFile.getFileName() + " "
+ (processOffset + mappedFileOffset));
break;
}
}
//当前的文件偏移量也就=fileFromOffset+读取到的文件偏移量
processOffset += mappedFileOffset;
this.mappedFileQueue.setFlushedWhere(processOffset);
this.mappedFileQueue.setCommittedWhere(processOffset);
//清理脏数据
this.mappedFileQueue.truncateDirtyFiles(processOffset);
//需要开启才去执行,非主流程,暂不研究
if (isExtReadEnable()) {
this.consumeQueueExt.recover();
log.info("Truncate consume queue extend file by max {}", maxExtAddr);
this.consumeQueueExt.truncateByMaxAddress(maxExtAddr);
}
}
}
recoverNormally 读懂了recoverConsumeQueue后这里就很好理解
public void recoverNormally(long maxPhyOffsetOfConsumeQueue) {
//校验crc
boolean checkCRCOnRecover = this.defaultMessageStore.getMessageStoreConfig().isCheckCRCOnRecover();
final List<MappedFile> mappedFiles = this.mappedFileQueue.getMappedFiles();
if (!mappedFiles.isEmpty()) {
//从最后第三个文件开始恢复
// Began to recover from the last third file
int index = mappedFiles.size() - 3;
if (index < 0)
index = 0;
MappedFile mappedFile = mappedFiles.get(index);
ByteBuffer byteBuffer = mappedFile.sliceByteBuffer();
//这个值不是0开始的,而是文件的名字的偏移量。所以下面是从文件的名字偏移量开始,不断自增消息的大小,就能得到这个文件目前的偏移量
long processOffset = mappedFile.getFileFromOffset();
//临时文件偏移
long mappedFileOffset = 0;
while (true) {
//循环读取文件的
DispatchRequest dispatchRequest = this.checkMessageAndReturnSize(byteBuffer, checkCRCOnRecover);
int size = dispatchRequest.getMsgSize();
// Normal data 成功并且有消息。当前临时文件偏移量增加
if (dispatchRequest.isSuccess() && size > 0) {
mappedFileOffset += size;
}
// Come the end of the file, switch to the next file Since the
// return 0 representatives met last hole,
// this can not be included in truncate offset
//说明当前读完了,要读下一个文件
else if (dispatchRequest.isSuccess() && size == 0) {
index++;
if (index >= mappedFiles.size()) {
//完全读完了 这是正常读完的流程
// Current branch can not happen
log.info("recover last 3 physics file over, last mapped file " + mappedFile.getFileName());
break;
} else {
//设置下一个文件开始读
mappedFile = mappedFiles.get(index);
byteBuffer = mappedFile.sliceByteBuffer();
processOffset = mappedFile.getFileFromOffset();
mappedFileOffset = 0;
log.info("recover next physics file, " + mappedFile.getFileName());
}
}
// Intermediate file read error 异常
else if (!dispatchRequest.isSuccess()) {
log.info("recover physics file end, " + mappedFile.getFileName());
break;
}
}
processOffset += mappedFileOffset;
this.mappedFileQueue.setFlushedWhere(processOffset);
this.mappedFileQueue.setCommittedWhere(processOffset);
//清理超出的文件
this.mappedFileQueue.truncateDirtyFiles(processOffset);
// Clear ConsumeQueue redundant data consumeQueue是conmitLog的索引文件。consumeQueue的偏移量如果超过了commitLog的偏移量的话。要清理consumeQueue的文件索引
if (maxPhyOffsetOfConsumeQueue >= processOffset) {
log.warn("maxPhyOffsetOfConsumeQueue({}) >= processOffset({}), truncate dirty logic files", maxPhyOffsetOfConsumeQueue, processOffset);
this.defaultMessageStore.truncateDirtyLogicFiles(processOffset);
}
} else {
// Commitlog case files are deleted
log.warn("The commitlog files are deleted, and delete the consume queue files");
this.mappedFileQueue.setFlushedWhere(0);
this.mappedFileQueue.setCommittedWhere(0);
this.defaultMessageStore.destroyLogics();
}
}
recoverAbnormally
这个方法只多了一个异常恢复代码,已经是过期代码。可以自己研读。后续有看会补充