一、消息的生产过程

1、Producer可以将消息写入到某Brocker中的某个Queue中,经历了如下过程:
  1. Producer先向NameServer发送获取消息Topic的路由信息请求;
  2. NameServer返回该topic的路由表以及Broker列表;
  3. Producer根据代码中指定的Queue列表选出一个队列,用于后续存储消息;
  4. Producer对消息进行处理,选择出的Queue所在的Broker发出RPC请求,将消息发送到选择的Queue中。
2、涉及到的名词解释:
  1. 路由表:实际是map,key为topic名称,value为QueueData实例列表。 QueueData并不是一个Queue对应一个QueueData,而是一个Broker中该Topic的所有Queue对应一个QueueData。简单来说,路由表key为topic,value为所有涉及该topic的BrokerName列表。
  2. Broker列表:实际也是一个map。key为brokerName,value为BrokerData。一套brokerName名称相同的Master-Slave小集群对应一个BrokerData。BrokerData中包含brokerName及一个map。该map的key为brokerId,value为该broker对应的地址。brokerId为0表示该broker为Master,非0为Slave。
3、对于一个无序消息,其Queue选择算法,也称为消息投递算法。常见的有两种:
  1. 轮训算法
    默认选择算法,保证每一个Queue均可以获取到消息。容易出现消息积压。
  2. 最小投递延迟算法
    每次消息投递记录时间延迟,将消息投递给上次时间最小的Queue,如果延迟相同,采用轮训算法。容易出现Queue分配不均匀,投递延迟小的Queue其可能会存在大量消息,而对该Queue的消费者压力会增大,降低消息的消费能力,可能会导致MQ中消息堆积。
4、消息的存储:
  1. 主流的消息存储方式:分布式KV存储、文件系统存储、关系型数据库。
  2. Rocket MQ中的消息存储在本地文件系统中,这些相关文件默认在当前用户主目录下的store目录中。

涉及到的名词解释:

  1. abort:该文件在Broker启动后自动创建,正常关闭Broker,该文件会自动消失。若在没有启动Broker的情况下,发现这个文件是存在的,则说明之前的Broker的关闭事非正常关闭。
  2. checkpoint:存储着commitlog、consumequeue、index文件的最后刷盘时间戳。
  3. commitlog:存放commitlog文件,而消息写在commitlog文件中的
  4. config:存放着Broker运行期间的一些配置数据。
  5. consumequeue:其中存放着 consumequeue 文件。
    5.1 index:其中存放着消息索引文件indexFile。
    5.2 lock:运行期间使用到的全局资源锁。
    5.3 commitlog文件(mappedFile)
    消息落盘位置,大小为1G,多余部分新建文件,文件名为20位十进制数构成,表示当前文件第一条起始偏移量。
    一个Broker中有一个commitlog目录,所有的mappedFile文件都存放在该目录中。无论当前Broker存放多少topic消息,这些消息都被顺序写入mappedFile文件中。顺序存取效率高于随机存取。
    消息单元

    5.4 consumequeue:消费队列

    为了提高效率,会为每个Topic在~/store/consumequeue中创建一个目录,目录名为topic名称。在该Topic目录下,会再为每个该Topic的Queue建立一个目录,目录名为queueId。每个目录中存放若干consumequeue文件,consumequeue文件是commitlog的索引文件,可以根据consumequeue定位到具体的消息。
    Consumequeue文件名也是由20位数字构成,表示当前文件的第一个索引条目的起始位置偏移量。与mappedFile文件名不同的是,后续文件名是固定的,因为consumequeue文件大小是固定不变的。
    索引条目:消息在mappedFile文件中的偏移量CommitLogOffset、消息长度、消息Tag的hashcode值。这三个属性占20个字节,所以每个文件大小都是固定的30w20字节。

    rocketmq springboot封装方法 rocketmq执行流程_数据


    rocketmq springboot封装方法 rocketmq执行流程_文件名_02


    5.5 对文件读写

    rocketmq springboot封装方法 rocketmq执行流程_文件名_03


    消息写入:
    Broker根据queueId获取到消息对应索引条目要在consumequeue目录写入偏移量(QueueOffset)。
    将queueId、queueOffset等数据,与消息一起封装为消息单元。
    将消息单元写入commit log。
    同时形成索引目录。
    将消息索引条目分发到相应的consumequeue。
    消息拉取:
    Consumer向Broker发送拉取请求,其中会包含要拉取消息的Queue、消息offset及消息Tag。
    消息offset=消费offset+1
    Broker计算在该consume queue中的queue Offset。
    queueoffset=消息offset
    20字节
    从该queue Offset处开始向后查找第一个指定Tag的索引条目。
    解析该索引条目的前8个字节,即可定位到该消息在commitlog中的commitlogoffset。
    从对应commitlogoffset中读取消息单元,并发送给Consumer。
    性能:RocketMQ对文件的读写通过mmap零拷贝进行,文件操作转换为对内存地址进行操作,提高文件的读写效率。
    consumequeue中的数据顺序存放,引入了pageCache的预读取机制,使得对consumequeue文件的读取接近内存读取,即使在消息堆积的情况下也不会影响性能。
    PageCache预读取机制:页缓存机制,加速对文件的读写操作。
    写操作:OS会将数据写入到PageCache中,以异步方式由pdflush内核线程将Cache中的数据刷盘到物理磁盘。
    读操作:首先从PageCache中读取,没有命中在从物理磁盘上加载该数据到PageCache的同时,也会顺序对其相邻数据块中的数据进行预读取。
    RocketMQ中会影响性能的是对commitlog文件的读取,因为是随机读取影响性能。如果选择合适的IO调度算法,比如设置调度算法为Deadline,随机性也会有所提升。
  6. index File
    store文件下index子目录中的index File,可以根据key进行对消息的查询。index File中的索引数据是在包含key的消息被发送到Broker时写入的,没有key不写入。
    索引条目结构:时间戳命名,文件由indexHeader,slots槽位,indexes索引数据组成。每个index File包含500w个slot槽,每个slot槽挂载很多的index索引单元。