rocketmq-消息存储(五)

系列文章目录

rocketmq—安装篇(一)
rocketmq—手把手搭建集群模式(二)
rocketmq—各类型消息实战(三)
rocketmq—名词解释(四)
rocketmq—消息存储(五)



前言

前面几章讲解了各个角色组成部分、集群搭建、发送各种类型消息等。但作为高可用的分布式消息队列,必定是要实现消息的持久化存储的。这篇文章就要讲rocketmq是如何实现持久化存储了,here we go … …


一 集群架构

1.1 集群架构图

把之前的角色部分又粘贴过来了,温故而知新,让老铁们加深点影响。
在这里插入图片描述
Nameserver:管理broker ,通过它知道从哪个broker读写数据。
Producer:生产者,发送消息
Broker:master slave ,负责消息存储、投递、查询
Consumer:消费者,消费消息
Topic:对消息进行分类,一个Producer可以发送消息给一个或者多个Topic,一个Consumer可以订阅一个或者多个Topic消息
Message Queue:相当于Topic 的分区,用于并行发送和接收消息。(同一个Topic 的消息,可以发送到多个Queue里面)

二 消息存储

2.1 消息传输过程

在这里插入图片描述

  1. 消息生产者发送消息到MQ。
  2. MQ收到消息,将消息进行持久化,即在存储系统中新增一条记录。
  3. 返回ACK确认消息给生产者。
  4. 然后MQ推送消息给对应的消费者,等待消费者返回ACK。
  5. 如果消息消费者在指定时间内成功返回ACK,那么MQ认为消息消费成功,在存储系统中删除消息,即执行第6步;如果MQ在指定时间内没有收到ACK,则认为消息消费失败,会尝试重新推送消息,重复执行4、5、6步骤。

2.2 存储介质

关系型数据库、KV非关系型数据库、文件系统

  • 普通关系型数据库(如Mysql)在单表数据量达到千万级别的情况下,其IO读写性能往往会出现瓶颈。在可靠性方面,该种方案非常依赖DB,如果一旦DB出现故障,则MQ的消息就无法落盘存储会导致线上故障。
  • 非关系型数据库,处理海量数据效率高,但是不支持事务、数据与数据间没有关系,适合处理海量数据,保证效率,不一定安全。
  • 消息刷盘至所部署虚拟机/物理机的文件系统来做持久化(刷盘一般可以分为异步刷盘和同步刷盘两种模式)为消息存储提供了一种高效率、高可靠性和高性能的数据持久化方式。除非部署MQ机器本身或是本地磁盘挂了,否则一般是不会出现无法持久化的故障问题。

性能对比: 文件系统>KV非关系型数据库>关系型数据库DB

2.3 消息存储与发送

2.3.1 消息存储

  • 目前高性能磁盘顺序写可以达到600MB/s,磁盘随机写大概只有100K/s。rocketmq的消息存储采用的就是顺序写,随机度的机制,保证了高效率。

2.3.2 消息发送

文件操作、网络操作需要涉用户态、内核态的切换。

一台服务器把本机磁盘文件的内容发送到另一个客户端,一般分为两个步骤:

  • read:读取本地文件内容;
  • write:将读取的内容通过网络发送出去。

这两个看似简单的操作,实际进行了4 次数据复制,分别是:

  • 从磁盘复制数据到内核态内存;
  • 从内核态内存复制到用户态内存;
  • 从用户态内存复制到网络驱动的内核态内存;
  • 从网络驱动的内核态内存复 制到网卡中进行传输。

通过使用“零拷贝”-mmap的方式,省去向【用户态】的内存复制,提高速度。这种机制在Java中是通过MappedByteBuffer实现的。

采用MappedByteBuffer这种内存映射的方式有几个限制,其中之一是一次只能映射1.5~2G的文件至用户态的虚拟内存,这也是为何RocketMQ默认设置单个CommitLog日志数据文件为1G的原因了。

2.4 消息存储结构

rocketmq 消息存储目录store下面的目录结构:

abort
checkpoint
config
|-- consumerFilter.json
|-- consumerOffset.json  ## 保存consumer消费进度
|-- delayOffset.json
|-- subscriptionGroup.json
|-- topics.json
commitlog
|-- 00000000000000000000
consumer
|-- HelloTopic
	|-- 0
	|-- 1
	|-- 2
	|-- 3
|-- TestTopic
	|-- 0
	|-- 1
	|-- 2
	|-- 3
|-- index
	|-- 20210116144023028

主要组成:

  • config:
    • consumerOffset.json 存储消息消费进度
  • CommitLog:存储消息的元数据。所有的topic的消息都存储在一个commitlog中,顺序写,随机读;受mmap文件内存映射限制,单个文件默认为1G大小。
    1、单个文件大小默认1G ,文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件;
  • ConsumerQueue:消息消费的逻辑队列。
    1、每个topic有自己的多个consumerqueue文件,每个文件里有多个msg,每个msg由CommitLog Offset、Size 和Msg Tag Hashcode组成;
    2、consumequeue里面单个msg里之所以存tag,是因为在进行Broker端消息过滤时比对tag决定是否消费消息;存tag的hashcode而不是字符串,是因为定长方式存储,节约空间。
  • IndexFile:存储消息的索引key,在实际的物理存储上,文件名则是以创建时的时间戳命名的。为了消息查询提供了一种通过key或时间区间来查询消息的方法,这种通过IndexFile来查找消息的方法不影响发送与消费消息的主流程。
    1、有了commitlog和consumequeue,消息发送和消费逻辑已经可以得到满足,引入index file 是为消息建立索引方便问题排查:在给定topic和key的前提下,快速定位到消息;msgid是有broker地址和commitlog offset组成,通过它也很容易定位到消息。(没有size如何取得完整的msg消息呢?
    2、根据key的hashcode取模slotNum,得到在slotTable中的具体位置。(那通过timestamp 如何定位槽位呢?通过文件的名称)
    3、每个slot value 指向该slot 对应的index Linked list 中最新的msg index(倒排索引,头插法);每个msg index 的next index offset 连接下一个msg index.
    4、固定的单个IndexFile文件大小约为400M,一个IndexFile可以保存 2000W个索引;
  • MappedFileQueue: 对连续物理存储的抽象封装类,可以通过消息存储的物理偏移量位置快速定位该offset所在MappedFile(具体物理存储位置的抽象)、创建、删除MappedFile等操作;
  • MappedFile: 对文件存储的直接内存映射抽象封装类(直接在os内核分配内存),通过操作该类,可以把消息写入pagecache或者原子性地将消息持久化刷盘。

详解见图:
在这里插入图片描述 在这里插入图片描述

2.5 刷盘方式

在这里插入图片描述

同步刷盘:

  • 消息通过java程序写入到系统缓冲区pagecache后,主线程唤醒同步刷盘线程刷盘,等刷盘完毕,刷盘线程唤醒等待线程,返回写成功的状态
  • 安全性高,吞吐量小

异步刷盘:

  • 消息通过java程序写入到系统缓冲区pagecache后,立刻返回成功的状态。
  • 写操作快,吞吐量大

配置:

#  SYNC_FLUSH、ASYNC_FLUSH 选择一个
flushDiskType = SYNC_FLUSH

三 问题

1、index file每个文件限定2000W个索引,怎么定的?

答:规定每个文件存储的索引个数最大为2000W。文件大小是固定的,等于40(header)+500W4(slot)+2000W20(索引)= 420000040个字节大小。

2、index file每个文件的slotNum 怎么确定的?

答:默认槽位500W个,每个固定4byte。平均每个slot存4个索引

3、查询的流程:查询的传入值除了key外,为什么还包含一个时间起始值以及截止值?

答:一个indexFile写完一个会继续写下一个,仅仅一个key无法定位到具体的indexFile,时间范围就为了更精确的定位到具体的indexFile,缩小查找的范围,indexFile文件名是一个时间戳,根据日期就可以定位到传入的日期范围对应在哪个或者哪些indexFile中

4、通过key+timestamp 在indexfile中找到对应的索引后,知晓了对应的commitlog Offset,但是没有size如何取得完整的msg消息呢?


源代码见:这里


我向你奔赴而来,你就是星辰大海

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值