1、消息存储分析
1.1 DefaultMessageStore 概要
其核心属性如下:
- messageStoreConfig
存储相关的配置,例如存储路径、commitLog文件大小,刷盘频次等等。 - CommitLog commitLog
comitLog 的核心处理类,消息存储在 commitlog 文件中。 - ConcurrentMap<String/* topic */, ConcurrentMap<Integer/* queueId */, ConsumeQueue>> consumeQueueTable
topic 的队列信息。 - FlushConsumeQueueService flushConsumeQueueService
ConsumeQueue 刷盘服务线程。 - CleanCommitLogService cleanCommitLogService
commitLog 过期文件删除线程。 - CleanConsumeQueueService cleanConsumeQueueService
consumeQueue 过期文件删除线程。、 - IndexService indexService
索引服务。 - AllocateMappedFileService allocateMappedFileService
MappedFile 分配线程,RocketMQ 使用内存映射处理 commitlog、consumeQueue文件。 - ReputMessageService reputMessageService
reput 转发线程(负责 Commitlog 转发到 Consumequeue、Index文件)。 - HAService haService
主从同步实现服务。 - ScheduleMessageService scheduleMessageService
定时任务调度器,执行定时任务。 - StoreStatsService storeStatsService
存储统计服务。 - TransientStorePool transientStorePool
ByteBuffer 池,后文会详细使用。 - RunningFlags runningFlags
存储服务状态。 - BrokerStatsManager brokerStatsManager
Broker 统计服务。 - MessageArrivingListener messageArrivingListener
消息达到监听器。 - StoreCheckpoint storeCheckpoint
刷盘检测点。 - LinkedList dispatcherList
转发 comitlog 日志,主要是从 commitlog 转发到 consumeQueue、index 文件。
上面这些属性,是整个消息存储的核心,也是我们需要重点关注与理解的(将会在本系列一一介绍到)。
接下来,先从 putMessage 为入口,一起探究整个消息存储全过程。
1.2 消息存储流程
1.2.1 DefaultMessageStore.putMessage
public PutMessageResult putMessage(MessageExtBrokerInner msg) {
if (this.shutdown) {
log.warn("message store has shutdown, so putMessage is forbidden");
return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);
}
if (BrokerRole.SLAVE == this.messageStoreConfig.getBrokerRole()) {
long value = this.printTimes.getAndIncrement();
if ((value % 50000) == 0) {
log.warn("message store is slave mode, so putMessage is forbidden ");
}
return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);
}
if (!this.runningFlags.isWriteable()) {
long value = this.printTimes.getAndIncrement();
if ((value % 50000) == 0) {
log.warn("message store is not writeable, so putMessage is forbidden " + this.runningFlags.getFlagBits());
}
return new PutMessageResult(PutMessageStatus.SERVICE_NOT_AVAILABLE, null);
} else {
this.printTimes.set(0);
}
if (msg.getTopic().length() > Byte.MAX_VALUE) {
log.warn("putMessage message topic length too long " + msg.getTopic().length());
return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, null);
}
if (msg.getPropertiesString() != null && msg.getPropertiesString().length() > Short.MAX_VALUE) {
log.warn("putMessage message properties length too long " + msg.getPropertiesString().length());
return new PutMessageResult(PutMessageStatus.PROPERTIES_SIZE_EXCEEDED, null);
}
if (this.isOSPageCacheBusy()) { //@1
return new PutMessageResult(PutMessageStatus.OS_PAGECACHE_BUSY, null);
}
long beginTime = this.getSystemClock().now();
PutMessageResult result = this.commitLog.putMessage(msg); // @2
long eclipseTime = this.getSystemClock().now() - beginTime;
if (eclipseTime > 500) {
log.warn("putMessage not in lock eclipse time(ms)={}, bodyLength={}", eclipseTime, msg.getBody().length);
}
this.storeStatsService.setPutMessageEntireTimeMax(eclipseTime); //@3
if (null == result || !result.isOk()) { //@4
this.storeStatsService.getPutMessageFailedTimes().incrementAndGet();
}
return result;
代码@1:检测操作系统页写入是否繁忙。
@Override
public boolean isOSPageCacheBusy() {
long begin = this.getCommitLog().getBeginTimeInLock();
long diff = this.systemClock.now() - begin;
if (diff < 10000000 //
&& diff > this.messageStoreConfig.getOsPageCach