RocketMQ核心源码解读

主要功能模块

  • broker: Broker 模块(broke 启动进程)
  • namesrv:NameServer模块
  • client :消息客户端,包含消息生产者、消息消费者相关类
  • store:消息存储模块
  • remoting:远程访问模块
  • example: RocketMQ 例代码

NameServer

NameServer 整体架构

在这里插入图片描述

Broker

Broker 整体架构

在这里插入图片描述

Netty的远程调用框架

在这里插入图片描述

Broker心跳注册管理

Broker 在启动时向所有 NameServer 注册自己的服务信息,并且会定时向 NameServer 发送心跳信息。

NameServer 会维护 Broker 的路由列表,并对路由列表进行实时更新。

启动 Broker 后会立即向 NameServer 进行心跳注册。通过定时任务,以 10s 为延迟,默认 30s 的间隔持续向 NameServer 发送心跳。

通过循环 nameServerAddressList 向每个 NameServer 单独注册 Broker。

Broker 心跳注册管理整体架构

在这里插入图片描述

Producer发送消息过程

  • DefaultMQProducer:只负责发送消息,发送完消息,就可以停止了。
  • TransactionMQProducer:支持事务消息机制。

DefaultMQProducer 启动与发送消息

在这里插入图片描述


Consumer拉取消息过程

  • 消费者分为推模式和拉模式消费者。重点关注推模式的消费者。
  • 消费者组之间有集群模式和广播模式两种。

Consumer 整体架构

在这里插入图片描述

消息持久化设计

RocketMQ的持久化文件结构

  • CommitLog:存储消息的元数据。所有消息都会顺序存入到- CommitLog文件当中。CommitLog由多个文件组成,每个文件固定大小1G。以第一条消息的偏移量为文件名。
  • ConsumerQueue:存储消息在CommitLog的索引。一个MessageQueue一个文件,记录当前MessageQueue被哪些消费者组消费到了哪一条CommitLog。
  • IndexFile:为了消息查询提供了一种通过key或时间区间来查询消息的方法,这种通过IndexFile来查找消息的方法不影响发送与消费消息的主流程

另外,还有几个辅助的存储文件,主要记录一些描述消息的元数据:

  • checkpoint:数据存盘检查点。里面主要记录commitlog文件、ConsumeQueue文件以及IndexFile文件最后一次刷盘的时间戳。
  • config/*.json:这些文件是将RocketMQ的一些关键配置信息进行存盘保存。例如Topic配置、消费者组配置、消费者组消息偏移量Offset 等等一些信息。
  • abort:这个文件是RocketMQ用来判断程序是否正常关闭的一个标识文件。正常情况下,会在启动时创建,而关闭服务时删除。但是如果遇到一些服务器宕机,或者kill -9这样一些非正常关闭服务的情况,这个abort文件就不会删除,因此RocketMQ就可以判断上一次服务是非正常关闭的,后续就会做一些数据恢复的操作。

文件保存大概流程

Producer 发过来的所有消息,不管属于哪个 Topic,Broker 都统一存在 CommitLog 文件中,然后分别构建 ConsumeQueue 和 IndexFile 两个索引文件,用来辅助消费者进行消息检索。

这种设计的优点:
可以较少查找目标文件的时间,让消息以最快的速度落盘。

commitLog 写入整体架构

在这里插入图片描述

文件同步刷盘和异步刷盘

通过 broker 的配置文件中 flushDiskType 参数决定刷盘方式,SYNC_FLUSH就是同步刷盘,ASYNC_FLUSH 就是异步刷盘。

RocketMQ 的同步刷盘不是来一条消息进行一次刷盘,而是隔 10ms 进行一次刷盘。

RocketMQ 的异步刷盘默认隔 200ms 进行一次刷盘。

两种方式刷盘失败,都会进行重试,确保刷盘成功,默认会重复提交 10 次。

分发ConsumeQueue和IndexFile

当 CommitLog 写入一条消息后,会启动一个后台线程,这个后台线程每隔 1ms 就去 CommitLog 中拉取最新消息,如果发现 CommitLog 中有新的消息写入,就会触发构建 ConsumeQueue 索引和 IndexFile 索引。

如果服务异常宕机,会造成 CommitLog, ConsumeQueue, IndexFile 不一致的情况,已经保存到 CommitLog 文件中,但没有分发到索引文件的内容就会丢失。


过期文件删除机制

在删除 CommitLog 文件时,Broker 会启动后台线程,每隔 60s,检查 CommitLog 文件,对超过 72h 的数据进行删除,在删除 CommitLog 文件时,并不会检查消息是否被消费过。

过期判断条件:
1、是否到了出发删除的时间,通过 broker.conf 的 deleteWhen 属性决定。
2、检查磁盘利用率是否达到阈值,如果达到了也会触发过期文件删除。


文件索引结构

CommitLog 文件

CommitLog 文件大小是固定的,但是存储的消息长度是不固定的,所以每次向 CommitLog 存入消息之前要检查 CommitLog 文件空间是否足够,如果不够的话,重新创建一个 CommitLog 文件。

ConsumeQueue 文件

ConsumeQueue 文件固定由 30 万个固定大小 20 byte 的数据块组成。

数据块结构:msgPhyOffset(8byte,消息在 CommitLog 文件中的起始位置) + msgSize(4byte,消息在 CommitLog 文件中占用的长度) + msgTagCode(8byte,消息的tag的Hash值)

因为 msgTagCode 存储的是 tag 的 Hash值,还仅有 8 位,可能出现哈希碰撞的情况。

是 CommitLog 的索引结构,加速消息读取。Consumer 消费消息时,Broker 就通过 ConsumeQueue 来找到 CommitLog 中消息。

IndexFile 文件

IndexFile 文件也是一个固定大小的文件,文件名是一个时间戳。

文件结构

  • indexHeader(固定40byte) :存放元信息,slot 数,时间范围,offset 范围等。
  • slot(固定500W个,每个固定20byte):用于快速查找 key 对应的位置。
  • index entry(最多500W*4个,每个固定20byte) :真正的倒排链表索引节点。
    • keyHash:key 的哈希值,用于查找。key 的 hash 冲突由倒排链表解决。
    • phyOffset:消息在 CommitLog 中的物理偏移量。
    • timeDiff:与当前文件起始时间的差值,单位是秒。
    • prevIndexOffset:上一个 index 的索引,形成链表结构。

是 RocketMQ 用于根据 key 快速查找消息的索引文件,构建了一个基于哈希的倒排索引结构。

延迟消息机制

RocketMQ 提供了两种延迟消息机制,一种是设置延迟级别,一共有 18 个延迟级别;另一种是设置固定的时间点,到时间后,RocketMQ 会自动发送消息。

设置延迟级别

18 个延迟级别对应 18 个 MessageQueue,这些队列被安排在默认的 Schedule_Topic 主题下。

当延迟消息发过来,在 Broker 处理这些消息之前,把这些消息保存到对应的队列中。

此时启动若干个定时任务,等到延迟时间到了,再把这些消息转发到原本属于的消息队列下,再推送给消费者。这个转储功能只在 master 节点启动,在 slave 节点上会关闭这个服务。

消息转发的过程是每隔 1s 扫描放入延迟队列的消息,查找对应的 ConsumeQueue 文件中还未处理过的消息,并计算他们的延迟时间,如果时间没到,等下一秒重新扫描,如果到了,把消息放回原来的 MessageQueue 下。

设置固定时间点

对指定时间点的延迟消息进行检查,如果还没有到指定时间点,把这条消息放入默认的 rmq_sys_wheel_timer Topic 中,队列固定为 0。

时间轮算法

核心思想

  • 数据按照预设的过期时间,放到对应的 slot 上,每个 slot 记录一条数据的索引。如果数据的延迟时间超过了时间轮的最大数据数,就会把 slot 上的轮次数加 1。

整个时间轮默认的 slot 个数为 7 * 24 * 60 * 60,即 7 天的秒数之和。这也是 RocketMQ 的延迟消息精确时间是 1 秒的原因。

  • 时间轮上设置一个指针变量,指针会按照固定的时间前进,当指针指向一个 slot 时,这个slot 的轮次数为0,说明这个 slot 上的数据已到期,如果轮次数大于 1,那么就把这个轮次数减 1。

时间轮上有两个指针,currWriteTimeMs 指向当前正在写入数据的 slot,currReadTimeMs 指向当前正在读取数据的 slot。

长轮询机制

RocketMQ 对消息消费者提供了 Push 和 Pull 两种消费模式,Push 的本质可以认为是一种定时的 Pull 机制。

长轮询机制为了解决当消费者一直向 Broker 发送 Pull 请求,但是 Broker 没有消息可发送造成的空轮询问题。

长轮询的机制是,当 Broker 接收到消费者的 Pull 请求时,如果没有可响应的消息,就把这个 Pull 请求内容缓存起来,当 Producer 发送消息过来时,首先判断这个消息是否可以响应已缓存的 Pull 请求,如果可以,就把 Pull 请求从缓存中拉出来,并把消息通知给消费者。

顺序写加速文件写入磁盘

顺序写加速文件写入磁盘。是因为在写入数据前,已经在磁盘中申请了一块连续的磁盘空间,并且写入地址保存到应用程序,不需要操作系统执行寻址操作,这样使文件写入磁盘速度很快。

零拷贝加速文件读写

mmap 文件映射机制

RocketMQ 使用 mmap 文件映射机制实现零拷贝。

  • 磁盘和内核态(文件页缓存)之间数据拷贝使用 DMA 拷贝。
  • 内核态和用户态,同种平台之间的数据拷贝用 CPU 拷贝。
  • 用户态不在保存内核态文件的内容,而是保存文件的内存起始地址,文件大小等。

sendFile 机制

Kafka 使用这种机制,这种机制和 mmap 机制的区别是,内核态页缓存和 socket 缓存之间的数据拷贝不再拷贝文件内容,而是拷贝文件起始地址和文件大小。

真实的数据会由 DMA 从页缓存中打包异步发送到 socket 中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值