文章目录
作用
前面介绍了RocketMQ的一些主要的日志文件,CommitLog,ConsumeQueue,IndexFile的结构和存储操作原理。这些文件的处理类都在不同的类中处理的。RocketMQ中提供了DefaultMessageStore
来对这些类进行一个封装聚合和额外的扩展。比如过期消息的清理,新消息的保存,消息的查询,消息的刷盘等。除此之外也提供一些服务启动时候的一些逻辑,比如从磁盘加载元数据,服务停止时候的消息保存逻辑等。
分析
构造方法DefaultMessageStore
DefaultMessageStore
的构造方法,主要是给该类的一些成员变量进行赋值或者初始化。这些成员变量都是DefaultMessageStore
间接操作CommitLog文件或者是ConsumeQueue的对象以及持久化等相关的操作类。
public DefaultMessageStore(final MessageStoreConfig messageStoreConfig, final BrokerStatsManager brokerStatsManager,
final MessageArrivingListener messageArrivingListener, final BrokerConfig brokerConfig) throws IOException {
//消息到达监听器,这个跟PullRequestHoldService一起使用的,消息到达后调用PullRequestHoldService中的notifyMessageArriving方法
this.messageArrivingListener = messageArrivingListener;
//broker配置
this.brokerConfig = brokerConfig;
//消息存储配置
this.messageStoreConfig = messageStoreConfig;
//broker状态管理类,统计broker相关信息,比如消息数量,topic数量等
this.brokerStatsManager = brokerStatsManager;
//提前主动申请内存文件服务类,用于CommitLog
this.allocateMappedFileService = new AllocateMappedFileService(this);
//
if (messageStoreConfig.isEnableDLegerCommitLog()) {
this.commitLog = new DLedgerCommitLog(this);
} else {
this.commitLog = new CommitLog(this);
}
//管理ConsumeQueue文件的集合
this.consumeQueueTable = new ConcurrentHashMap<>(32);
//将ConsumeQueue逻辑队列刷盘的服务类,1秒刷一次,最多尝试3次,
this.flushConsumeQueueService = new FlushConsumeQueueService();
//清理物理文件服务,定期清理72小时之前的物理文件。
this.cleanCommitLogService = new CleanCommitLogService();
//清理逻辑文件服务,定期清理在逻辑队列中的物理偏移量小于commitlog中的最小物理偏移量的数据,同时也清理Index中物理偏移量小于commitlog中的最小物理偏移量的数据
this.cleanConsumeQueueService = new CleanConsumeQueueService();
//存储层内部统计服务
this.storeStatsService = new StoreStatsService();
//index文件的存储
this.indexService = new IndexService(this);
//用于commitlog数据的主备同步
if (!messageStoreConfig.isEnableDLegerCommitLog()) {
this.haService = new HAService(this);
} else {
this.haService = null;
}
//用于转发CommitLog中消息到ConsumeQueue中
this.reputMessageService = new ReputMessageService();
//用于监控延迟消息,并到期后执行,吧延迟消息写入CommitLog
this.scheduleMessageService = new ScheduleMessageService(this);
//临时日志文件存储池
this.transientStorePool = new TransientStorePool(messageStoreConfig);
if (messageStoreConfig.isTransientStorePoolEnable()) {
this.transientStorePool.init();
}
//启动allocateMappedFileService
this.allocateMappedFileService.start();
//启动indexService
this.indexService.start();
//消息转存任务的集合
this.dispatcherList = new LinkedList<>();
//负责把CommitLog消息转存到ConsumeQueue文件
this.dispatcherList.addLast(new CommitLogDispatcherBuildConsumeQueue());
//负责吧CommitLog消息转存到Index文件
this.dispatcherList.addLast(new CommitLogDispatcherBuildIndex());
//在保存日志数据文件的根目录 {user.home}/store 路径下 创建一个一个名字为lock 的文件
File file = new File(StorePathConfigHelper.getLockFile(messageStoreConfig.getStorePathRootDir()));
MappedFile.ensureDirOK(file.getParent());
//创建根目录文件的随机文件流,权限是读写
lockFile = new RandomAccessFile(file, "rw");
}
这里对一些重要的变量进行讲解
变量对象 | 作用描述 |
---|---|
messageArrivingListener |
消息到达监听器,这个跟PullRequestHoldService 一起使用的,消息到达后调用PullRequestHoldService 中的notifyMessageArriving 方法 ,在push模式下会调用 |
brokerConfig |
broker配置 |
messageStoreConfig |
消息存储相关配置 |
brokerStatsManager |
broker状态管理类,统计broker相关信息,比如消息数量,topic数量等 |
allocateMappedFileService |
提前主动申请内存文件服务类,用于CommitLog |
commitLog |
CommitLog日志文件的生成和处理类,根据不同的情况可能是普通的CommitLog 页可能是基于Raft协议扩展实现的CommitLog |
consumeQueueTable |
管理ConsumeQueue文件的集合 |
flushConsumeQueueService |
将ConsumeQueue逻辑队列刷盘的服务类,1秒刷一次,最多尝试3次, |
cleanCommitLogService |
清理物理文件CommitLog服务,定期清理72小时之前的物理文件。或者CommitLog日志文件的在磁盘占比达到了75以上就会进行清理 |
cleanConsumeQueueService |
清理逻辑文件服务,定期清理在逻辑队列中的物理偏移量小于commitlog中的最小物理偏移量的数据,同时也清理Index中物理偏移量小于commitlog中的最小物理偏移量的数据 |
storeStatsService |
存储层内部统计服务 |
indexService |
index文件的存储和读取类 |
haService |
高可用的服务类,如果是基于Raft协议的高可用那么这个类就是null |
reputMessageService |
用于转发CommitLog中消息到ConsumeQueue中 |
scheduleMessageService |
用于监控延迟消息,并到期后执行,吧延迟消息真实的信息写入CommitLog |
transientStorePool |
临时日志文件存储池,在CommitLog使用的临时存储池的时候会用到 |
dispatcherList |
消息转存任务的集合 , 用于把CommitLog的消息日志同时转存一份到ConsumeQueue中和Index中,用于构建索引和消费队列 |
内部方法分析
这里分析Broker按照启动的前后顺序来进行分析。
Broker启动后服务于日志相关的类启动的start
方法
Broker在启动时会创建一个DefaultMessageStore
,利用前面说的构造器进行创建,然后会调用start
方法对成员变量中的一些类进行启动。
public void start() throws Exception {
//获取lock文件的唯一FileChannel对象,然后获取文件锁
lock = lockFile.getChannel().tryLock(0, 1, false);
//如果锁是null 或者 锁是共享类型的 或者 锁不是有效的,则抛出异常
if (lock == null || lock.isShared() || !lock.isValid()) {
throw new RuntimeException("Lock failed,MQ already started");
}
//写入lock 四个字符到lock文件
lockFile.getChannel().write(ByteBuffer.wrap("lock".getBytes()));
lockFile.getChannel()