前言
整理了一些RocketMQ相关流程图/原理图,做一下笔记,大家一起学习。
RocketMQ是什么是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式特点。
Producer、Consumer、队列都可以分布式。
Producer向一些队列轮流发送消息,队列集合称为Topic,Consumer如果做广播消费,则一个consumer实例消费这个Topic对应的所有队列,如果做集群消费,则多个Consumer实例平均消费这个topic对应的队列集合。
能够保证严格的消息顺序
提供丰富的消息拉取模式
高效的订阅者水平扩展能力
实时的消息订阅机制
亿级消息堆积能力
较少的依赖
RocketMQ核心组件图RocketMQ是开源的消息中间件,它主要由NameServer,Producer,Broker,Consumer四部分构成。
NameServer
NameServer主要负责Topic和路由信息的管理,功能类似Dubbo的zookeeper。
Producer
消息生产者,负责产生消息,一般由业务系统负责产生消息。
Broker
消息中转角色,负责存储消息,转发消息。
Consumer
消息消费者,负责消息消费,一般是后台系统负责异步消费。
RokcetMQ物理部署图NameServer
NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。
Broker
Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。
Producer
Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。
Consumer
Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。
RocketMQ逻辑部署结构
ProducerGroup
用来表示一个发送消息应用,一个ProducerGroup下包含多个Producer实例,可以是多台机器,也可以是一台机器的多个进程,或者一个进程的多个Producer对象。一个ProducerGroup可以发送多个Topic消息,ProducerGroup作用如下:
标识一类Producer
可以通过运维工具查询这个发送消息应用下有多个Producer实例
发送分布式事务消息时,如果Producer中途意外宕机,Broker会主动回调ProducerGroup内的任意一台机器来确认事务状态。
ConsumerGroup
用来表示一个消费消息应用,一个ConsumerGroup下包含多个Consumer实例,可以是多台机器,也可以是多个进程,或者是一个进程的多个Consumer对象。一个ConsumerGroup下的多个Consumer以均摊方式消费消息,如果设置为广播方式,那么这个ConsumerGroup下的每个实例都消费全量数据。
NameServer路由注册、删除机制Broker每30秒向NameServer发送心跳包,心跳包中包含topic的路由信息
NarneServer收到Broker心跳包后更新brokerLiveTable中的信息,特别记录心跳时间lastUpdateTime
NarneServer每隔10s扫描brokerLiveTable,检测表中上次收到心跳包的时间,比较当前时间与上一次时间,如果超过120s,则认为broker不可用,移除路由表中与该broker相关的所有信息
消息生产者拉取主题的路由信息,即消息生产者并不会立即感知Broker服务器的新增与删除。
RocketMQ的消息领域模型图Topic
Topic表示消息的第一级类型,比如一个电商系统的消息可以分为:交易消息、物流消息等。一条消息必须有一个Topic。
最细粒度的订阅单位,一个Group可以订阅多个Topic的消息。
Tag
Tag表示消息的第二级类型,比如交易消息又可以分为:交易创建消息,交易完成消息等。RocketMQ提供2级消息分类,方便灵活控制。
Group
组,一个组可以订阅多个Topic。
MessageQueue
消息的物理管理单位。一个Topic下可以有多个Queue,Queue的引入使得消息的存储可以分布式集群化,具有了水平扩展能力。
在RocketMQ中,所有消息队列都是持久化,长度无限的数据结构,所谓长度无限是指队列中的每个存储单元都是定长,访问其中的存储单元使用Offset来访问,offset为javalong类型,64位,理论上在100年内不会溢出,所以认为是长度无限。
也可以认为MessageQueue是一个长度无限的数组,Offset就是下标。
顺序消息原理图消费消息的顺序要同发送消息的顺序一致,在RocketMQ中,主要的是局部顺序,即一类消息为满足顺序性,必须Producer单线程顺序发送,且发送到同一个队列,这样Consumer就可以按照Producer收送的顺序去消费消息。
RocketMQ消息存储设计原理图CommitLog
消息存储文件,所有消息主题的消息都存储在CommitLog文件中。Commitlog文件存储的逻辑视图如图所示
ConsumeQueue
消息消费队列,消息到达CommitLog文件后,将异步转发到消息消费队列,供消息消费者消费。ConsumeQueue存储格式如下:
单个ConsumeQueue文件中默认包含30万个条目,单个文件的长度为30w×20字节,单个ConsumeQueue文件可以看出是一个ConsumeQueue条目的数组,其下标为ConsumeQueue的逻辑偏移量,消息消费进度存储的偏移量即逻辑偏移量。
ConsumeQueue即为Commitlog文件的索引文件,其构建机制是当消息到达Commitlog文件后,由专门的线程产生消息转发任务,从而构建消息消费队列文件与下文提到的索引文件。
IndexFile消息索引文件,主要存储消息Key与Offset的对应关系。
消息消费队列是RocketMQ专门为消息订阅构建的索引文件,提高根据主题与消息队列检索消息的速度,另外RocketMQ引入了Hash索引机制为消息建立索引,HashMap的设计包含两个基本点:Hash槽与Hash冲突的链表结构。RocketMQ索引文件布局如图所示
lndexFile总共包含lndexHeader、Hash槽、Hash条目
事务状态服务
存储每条消息的事务状态。
定时消息服务
每一个延迟级别对应一个消息消费队列,存储延迟队列的消息拉取进度。
RMQ文件存储模型层RocketMQ业务处理器层
Broker端对消息进行读取和写入的业务逻辑入口,这一层主要包含了业务逻辑相关处理操作(根据解析RemotingCommand中的RequestCode来区分具体的业务操作类型,进而执行不同的业务处理流程),比如前置的检查和校验步骤、构造MessageExtBrokerInner对象、decode反序列化、构造Response返回对象等。
RocketMQ数据存储组件层
该层主要是RocketMQ的存储核心类—DefaultMessageStore,其为RocketMQ消息数据文件的访问入口,通过该类的“putMessage()”和“getMessage()”方法完成对CommitLog消息存储的日志数据文件进行读写操作(具体的读写访问操作还是依赖下一层中CommitLog对象模型提供的方法);
另外,在该组件初始化时候,还会启动很多存储相关的后台服务线程,包括AllocateMappedFileService(MappedFile预分配服务线程)、ReputMessageService(回放存储消息服务线程)、HAService(Broker主从同步高可用服务线程)、StoreStatsService(消息存储统计服务线程)、IndexService(索引文件服务线程)等。
RocketMQ存储逻辑对象层
该层主要包含了RocketMQ数据文件存储直接相关的三个模型类IndexFile、ConsumerQueue和CommitLog。
IndexFile为索引数据文件提供访问服务,ConsumerQueue为逻辑消息队列提供访问服务,CommitLog则为消息存储的日志数据文件提供访问服务。
这三个模型类也是构成了RocketMQ存储层的整体结构。
封装的文件内存映射层
RocketMQ主要采用JDKNIO中的MappedByteBuffer和FileChannel两种方式完成数据文件的读写。
其中,采用MappedByteBuffer这种内存映射磁盘文件的方式完成对大文件的读写,在RocketMQ中将该类封装成MappedFile类。
这里,每一种类的单个文件均由MappedFile类提供读写操作服务(其中,MappedFile类提供了顺序写/随机读、内存数据刷盘、内存清理等和文件相关的服务)。
磁盘存储层
主要指的是部署RocketMQ服务器所用的磁盘。这里,需要考虑不同磁盘类型(如SSD或者普通的HDD)特性以及磁盘的性能参数(如IOPS、吞吐量和访问时延等指标)对顺序写/随机读操作带来的影响。
RocketMQ中消息刷盘在RocketMQ中消息刷盘主要可以分为同步刷盘和异步刷盘两种。
同步刷盘
在返回写成功状态时,消息已经被写入磁盘。
具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态。
一般只用于金融场景。
异步刷盘
在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE,写操作的返回快,吞吐量大;当内存里的消息量积累到一定程度时,统一触发写磁盘操作,快速写入。消息在系统中流转图
1.Producer发送消息,消息从socket进入java堆。
2.Producer发送消息,消息从java堆转入PAGACACHE,物理内存。
3.Producer发送消息,由异步线程刷盘,消息从PAGECACHE刷入磁盘。
4.Consumer拉消息(正常消费),消息直接从PAGECACHE(数据在物理内存)转入socket,到达consumer,不经过java堆。这种消费场景最多,线上96G物理内存,按照1K消息算,可以在物理内存缓存1亿条消息。
5.Consumer拉消息(异常消费),消息直接从PAGECACHE(数据在虚拟内存)转入socket。
6.Consumer拉消息(异常消费),由于Socket访问了虚拟内存,产生缺页中断,此时会产生磁盘IO,从磁盘Load消息到PAGECACHE,然后直接从socket发出去。
7.同5一致。
8.同6一致。
作者:Jay_huaxiao