之前4.8.0时,发过文章《RocketMQ 刷盘机制BUG踩坑——可能引起消息丢失》,说明了可能导致消息丢失的bug。有兴趣可以看看。
后来社区一直没改,我们就自己修复了,之后也没太关注新版本。
最近考察5版本,发现问题依旧。直接附代码DefaultMappedFile.java的flush方法。下面依然是catch了代码后没有做任何处理。
public int flush(final int flushLeastPages) {
if (this.isAbleToFlush(flushLeastPages)) {
if (this.hold()) {
int value = getReadPosition();
try {
this.mappedByteBufferAccessCountSinceLastSwap++;
//We only append data to fileChannel or mappedByteBuffer, never both.
if (writeBuffer != null || this.fileChannel.position() != 0) {
this.fileChannel.force(false);
} else {
this.mappedByteBuffer.force();
}
this.lastFlushTime = System.currentTimeMillis();//这里的lastFlushTime并没有用到
} catch (Throwable e) {//这里依然是catch了,但是没有做任何操作
log.error("Error occurred when force data to disk.", e);
}
FLUSHED_POSITION_UPDATER.set(this, value);
this.release();
} else {
log.warn("in flush, hold failed, flush offset = " + FLUSHED_POSITION_UPDATER.get(this));
FLUSHED_POSITION_UPDATER.set(this, getReadPosition());
}
}
return this.getFlushedPosition();
}
这就导致如果刷盘超时,是可以捕获的。但如果是刷盘直接报错,输出一条日志就完了,返回给客户端的还是SEND_OK。此时做个服务器重启,数据就丢了。
不过不得不承认,5版本可以自动切换主从,这样的话主节点丢失的数据从节点还有。但如果你想用单点的架构,那么消息丢失就是要考虑的点。