架构图
RocketMQ架构上主要分为四部分
Producer:消息发布的角色,支持分布式集群方式部署。Producer通过MQ的负载均衡模块选择相应的Broker集群队列进行消息投递,投递的过程支持快速失败并且低延迟。
Consumer:消息消费的角色,支持分布式集群方式部署。支持以push推,pull拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制,可以满足大多数用户的需求
NameServer:NameServer是一个非常简单的Topic路由注册中心,其角色类似Dubbo中的zookeeper,支持Broker的动态注册与发现。主要包括两个功能:Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;路由信息管理,每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费。NameServer通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker是向每一台NameServer注册自己的路由信息,所以每一个NameServer实例上面都保存一份完整的路由信息。当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息,Producer和Consumer仍然可以动态感知Broker的路由的信息
BrokerServer:Broker主要负责消息的存储、投递和查询以及服务高可用保证,为了实现这些功能,Broker包含了以下几个重要子模块。
Remoting Module:整个Broker的实体,负责处理来自Client端的请求。
Client Manager:负责管理客户端(Producer/Consumer)和维护Consumer的Topic订阅信息。
Store Service:提供方便简单的API接口处理消息存储到物理硬盘和查询功能。
HA Service:高可用服务,提供Master Broker 和 Slave Broker之间的数据同步功能。
Index Service:根据特定的Message key对投递到Broker的消息进行索引服务,以提供消息的快速查询。
架构部署
- **RocketMQ 网络部署特点**
- NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步
- Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave 的对应关系通过指定相同的BrokerName,不同的BrokerId 来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。 注意:当前RocketMQ版本在部署架构上支持一Master多Slave,但只有BrokerId=1的从服务器才会参与消息的读负载
- Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic 服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署
- Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,消费者在向Master拉取消息时,Master服务器会根据拉取偏移量与最大偏移量的距离(判断是否读老消息,产生读I/O),以及从服务器是否可读等因素建议下一次是从Master还是Slave拉取
- **集群工作流程**
- 启动NameServer,NameServer起来后监听端口,等待Broker、Producer、Consumer连上来,相当于一个路由控制中心
- Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有Topic信息。注册成功后,NameServer集群中就有Topic跟Broker的映射关系
- 收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic
- Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前发送的Topic存在哪些Broker上,轮询从队列列表中选择一个队列,然后与队列所在的Broker建立长连接从而向Broker发消息
- Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息
角色介绍
消息存储
息存储整体架构
CommitLog
ConsumeQueue
IndexFile
过期文件删除机制
CommitLog、ConsumerQueue文件是基于内存映射机制并在启动的时候回加
载CommitLog、ConsumerQueue目录下的所有文件,为了避免内存与磁盘的浪费,不可能将消息永
久存储在消息服务器上,所以要引入一种机制来删除已过期的文件。RocketMQ顺序写CommitLog、
ConsumerQueue文件,所有写操作全部落在最后一个CommitLog或者ConsumerQueue文件上,之前
的文件在下一个文件创建后将不会再被更新。RocketMQ清除过期文件的方法时:如果当前文件在在一
定时间间隔内没有再次被消费,则认为是过期文件,可以被删除,RocketMQ不会关注这个文件上的消
息是否全部被消费。默认每个文件的过期时间为72小时,通过在Broker配置文件中设置
fileReservedTime来改变过期时间,单位为小时。
页缓存与内存映射
零拷贝(mmap)
mmap将一个文件或者其它对象映射进内存。
它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。
而Posix或System V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一
mmap将一个文件或者其它对象映射进内存。
文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清
零。mmap在用户空间映射调用系统中作用很大,mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存
普通文件被映射到进程地址空间后,进程可以像访问普通内存(ByteByffer)一样对文件进行访问,通
过put和get进行访问不必再调用read(),write()等操作
PageCache
读Cache
- 当内核发起一个读请求时, 先会检查请求的数据是否缓存到了page cache中
- 如果有,那么直接从内存中读取,不需要访问磁盘, 此即 cache hit(缓存命中)
- 如果没有, 就必须从磁盘中读取数据, 然后内核将读取的数据再缓存到cache中, 如此
后续的读请求就可以命中缓存了
- page可以只缓存一个文件的部分内容, 而不需要把整个文件都缓存进来
写Cache
cache回收
cache和buffer的区别
Cache:缓存区,是高速缓存,是位于CPU和主内存之间的容量较小但速度很快的存储器,因
为CPU的速度远远高于主内存的速度,CPU从内存中读取数据需等待很长的时间,而 Cache
保存着CPU刚用过的数据或循环使用的部分数据,这时从Cache中读取数据会更快,减少了
CPU等待的时间,提高了系统的性能
Buffer:缓冲区,用于存储速度不同步的设备或优先级不同的设备之间传输数据;通过buffer
可以减少进程间通信需要等待的时间,当存储速度快的设备与存储速度慢的设备进行通信时,
存储慢的数据先把数据存放到buffer,达到一定程度存储快的设备再读取buffer的数据,在此
期间存储快的设备CPU可以干其他的事情。
HeapByteBuffer和DirectByteBuffer
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3SkDWHRR-1660545962393)(img\image-20220815113429434-166053447090311.png)]
RocketMQ 的所有消息都是持久化的,先写入系统 PageCache,然后刷盘,可以保证内存与磁盘
都有一份数据, 访问时,直接从内存读取。消息在通过Producer写入RocketMQ的时候,有两种写磁
盘方式,分布式同步刷盘和异步刷盘。
同步刷盘与异步刷盘的唯一区别是异步刷盘写完 PageCache直接返回,而同步刷盘需要等待刷盘
完成才返回, 同步刷盘流程如下
异步刷盘