RocketMQ
版本:rocketmq-4.2.0
bug所表现形式:在同步刷盘时,生产消息,返回SendResult的SendStatus为FLUSH_DISK_TIMEOUT,而且是在发送消息总量大概mapedFileSizeCommitLog(默认配置1G)的时候出现,每次达到mapedFileSizeCommitLog大小左右的时候都会出现FLUSH_DISK_TIMEOUT。而其余时间并没有出现状态,总是如此这显然是有问题的。
rocketmq刷盘逻辑:引用:https://blog.csdn.net/prestigeding/article/details/79188383
RocketMQ是如何判断flushOK?
刷盘:CommitLog.this.mappedFileQueue.flush(0)
原理:根据刷盘起始点【CommitLog.this.mappedFileQueue.getFlushedWhere()】和下次刷盘点【req.getNextOffset()】的比较来判断是否成功刷入磁盘。由于可能刷盘需要刷两个MappedFile,故循环次数为2。
代码:
private void doCommit() { synchronized (this.requestsRead) { if (!this.requestsRead.isEmpty()) { for (GroupCommitRequest req : this.requestsRead) { // There may be a message in the next file, so a maximum of // two times the flush boolean flushOK = false; for (int i = 0; i < 2 && !flushOK; i++) { flushOK = CommitLog.this.mappedFileQueue.getFlushedWhere() >= req.getNextOffset(); if (!flushOK) { CommitLog.this.mappedFileQueue.flush(0); } } req.wakeupCustomer(flushOK); } long storeTimestamp = CommitLog.this.mappedFileQueue.getStoreTimestamp(); if (storeTimestamp > 0) { CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(storeTimestamp); } this.requestsRead.clear(); } else { // Because of individual messages is set to not sync flush, it // will come to this process CommitLog.this.mappedFileQueue.flush(0); } } }
|
先讲正常流程:进入for循环,第一次:由于刷盘起始点【CommitLog.this.mappedFileQueue.getFlushedWhere()】小于下次刷盘点【req.getNextOffset()】,故flushOK为false,执行刷盘操作。第二次:由于刷盘成功,刷盘起始点【CommitLog.this.mappedFileQueue.getFlushedWhere()】等于下次刷盘点【req.getNextOffset()】,flushOK为true。结束循环。
bug出现了:若有两个MappedFile来存储,第一次flushOK为false,刷盘之后刷盘起始点【CommitLog.this.mappedFileQueue.getFlushedWhere()】还是小于下次刷盘点【req.getNextOffset()】,故开始第二次刷盘。而第二次刷盘成功,而这时循环却结束了。可flushOK还是为false。
修改代码:在for循环外再加flushOK的判断。