RocketMQ源码学习笔记:Broker启动流程

这是本人学习的总结,主要学习资料如下

1、Broker启动流程

Broker启动流程与NameServer的几乎一样,都是先读取外部配置并利用外部配置创建出BrokerController

接着使用创建好的BrokerController,在其initialize()方法中创建运行启动时必要的实例。

之后再start()方法中开启网络和持久化服务。

流程如下所示。

在这里插入图片描述


2、一些重要的类

在这里插入图片描述
以上是Broker中比较重要的类,他们被分成三个层次。

业务层是用于处理发送和接受信息,他们会调用逻辑存储层的对象获取基础一些的服务。

逻辑存储层的类可以看成是代表着Broker里的文件或者文件夹的,比如CommitLog这个类就代表着存储消息详细信息的commitlog文件夹;ConsumeQueue则代表存储消息队列的comsumequeue

存储I/O层里则是Broker里最贴近底层的类,比如MappedFile代表着某一个文件。

2.1、MappedFile

MappedFile代表着一个物理文件。

它是对File类的一个包装,底层还是用File访问磁盘文件。不过MappedFile增加了一些功能,比如增加了wrotePosition这个成员,代表着下次写文件时,系统可以直接通过wrotePosition知道文件的末尾在哪,这样就可以直接写入文件。

在这里插入图片描述

2.2、MessgeStore

MessageStore是用于读写存储文件的一个类。这里的存储文件是指CommitLogcomsumeQueue这些文件。

源码中使用它的实现类DefaultMessageStore,通过load()方法完成初始化,之后在开启服务的start()方法中通过start()方法开启服务。

所以MessageStore是管理存储文件的一个类。源码中还有多个和它类似的成员,他们负责不同的功能,但都是load()初始化,start()开启服务。


2.3、MessageStore的加载启动流程

MessageStore的启动加载包含了很多很多服务,比如CommitLogConsumeQueue等。但他们的加载启动都大同小异,所以这里只选了CommitLog讲解。

MessageStoreBrokerController的一个成员变量。它在BrokerController中的initialize()完成实例初始化,随后调用其load()方法更进一步地加载具体的服务。以下是涉及到的代码片段。

在这里插入图片描述


以这个线索来看CommitLog加载的具体内容,messageStore.load() -> commitLog.load() -> doLoad()

可以看出所谓的加载,就是访问/store/commitlog下的所有文件,将他们包装成MappedFile存起来,方便后续的访问。

3、技术亮点

3.1、 内存映射

3.1.1、简介

MappedFileBroker中可以说是最底层的代表文件的类,它使用了内存映射技术大大加快了文件的读写速度。

下面是网络数据到磁盘的过程。

在这里插入图片描述
一般的IO会有四次复制,两次DMA拷贝,两次CPU拷贝。

CPU拷贝的效率要慢很多,一般来说200M的数据,DMA拷贝仅需2ms,而CPU拷贝需要200ms。所以200M的数据从网络设备缓冲区到磁盘用传统的IO需要404ms

内存映射技术是建立一个磁盘空间和内存空间的映射通道,会覆盖一片磁盘空间,最大是1.5G ~ 2G

当我们向被覆盖的内存空间写数据时,数据可以通过通道到达磁盘。这种方式允许DMA直接从内存拷贝数据到磁盘。

所以应用内存映射技术后,数据从网络设备缓冲区到磁盘就只需要一次CPU拷贝,两次DMA拷贝。200m的数据只需要204ms,是传统IO的一半。

因为内存映射最多只能覆盖1.5G ~ 2G的磁盘空间,所以commitlog的文件最大是1G,保证每次映射能完整覆盖一个文件。


3.1.2、源码

内存映射的代码是放在MappedFile中,在BrokerController.initialize()阶段建立各个文件的内存映射通道。可根据一下的线索看到源码。

start() -> createBrokerController() -> controller.intialize() -> messageStore.load() -> commitLog.load() -> mappedFileQueue.load() -> doLoad() -> new MappedFile() -> init()

所以MappedFile在实例初始化时就会建立内存映射通道,以下是构造方法调用的init()的关键内容。

private void init(final String fileName, final int fileSize) throws IOException {
    this.file = new File(fileName);
    try {
    	// rw表示允许读写
        this.fileChannel = new RandomAccessFile(this.file, "rw").getChannel();
        this.mappedByteBuffer = this.fileChannel.map(MapMode.READ_WRITE, 0, fileSize);
        TOTAL_MAPPED_VIRTUAL_MEMORY.addAndGet(fileSize);
        TOTAL_MAPPED_FILES.incrementAndGet();
        ok = true;
    } catch (FileNotFoundException e) {
        log.error("Failed to create file " + this.fileName, e);
        throw e;
    } finally {
        if (!ok && this.fileChannel != null) {
            this.fileChannel.close();
        }
    }
}

可以看到,它通过new RandomAccessFile(this.file, "rw").getChannel()创建文件的内存通道,并且赋值给fileChannel。即使没有看其他的源码也可以知道,之后涉及到文件的读写操作最后一定会先获取这个fileChannel,再调用其中的read(),write()等方法进行读写。

其中还有一个叫mappedByteBuffer的成员变量,它代表着被映射的磁盘空间。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值