文章目录
rocketMq
物理部署结构
-
name Server 简化版的zookeeper注册中心,可集群部署,但是节点之间无任何信息的同步
-
broker分为master和slave, 一个master可对应多个slave,但是一个slaver只能对应一个master. Master 与 Slave 的对应关系通过指定相同的 BrokerName,不同的 BrokerId 来定义,BrokerId为 0 表示 Master,非 0 表示 Slave。
master也可以部署多个,每个broker都与Name server集群中的所有节点建立长连接,定期注册Topic信息到name server上.
-
producer 生产者, 于name Server集群中的其中一个(随机选择)节点建立长连接,定期中Name Server中获取对应Topic的路由信息.并向提供对应Topic服务的Master建立长连接,定时向Master发送心跳.
-
Consumer 消费者,同样会与NameServer集群中的其中一个节点(随机选择)建立长连接,定期中Name Server中获取Topic的路由信息.并向提供Topic服务的Mster、slave 建立长连接.且定时向Master 、Slave发送心跳. Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker决定.
逻辑部署结构
-
Producer Group
用来表示一个发送消息的应用,一个Producer Group下包含多个Producer实例,可以是多台机器,也可以是一台机器的多个进程,躲着一个进程的多个Producer对象.一个Producer Group可以发送多个Topic消息.Producer Group作用如下
-
标识一类 Producer
-
可以通过运维工具查询这个发送消息应用下有多个 Producer 实例
-
发送分布式事务消息时,如果 Producer 中途意外宕机,Broker 会主动回调 Producer Group 内的任意一台机器来确认事务状态。
-
-
Cousumer Group
用来表示一组消费消息的应用.一个 Consumer Group 下包含多个 Consumer 实例,可以是多台机器,也可以是多个进程,或者是一个进程的多个 Consumer 对象。
集群模式一个 Consumer Group 下的多个 Consumer 以均摊方式消费消息,如果设置为广播方式,那么这个 Consumer Group 下的每个实例都消费全量数据。
持久化方式
存储介质
市面上主流的消息队列对于消息的持久化主要有两种方案,一种是关系型数据库DB,一种是文件系统
-
关系型数据库DB
Apache下开源的另外一款MQ—ActiveMQ(默认采用的KahaDB做消息存储)可选用JDBC的方式来做消息持久化,通过简单的xml配置信息即可实现JDBC消息存储。由于,普通关系型数据库(如Mysql)在单表数据量达到千万级别的情况下,其IO读写性能往往会出现瓶颈。在可靠性方面,该种方案非常依赖DB,如果一旦DB出现故障,则MQ的消息就无法落盘存储会导致线上故障
-
文件系统
目前业界较为常用的几款产品(RocketMQ/Kafka/RabbitMQ)均采用的是消息刷盘至所部署虚拟机/物理机的文件系统来做持久化(刷盘一般可以分为异步刷盘和同步刷盘两种模式)。消息刷盘为消息存储提供了一种高效率、高可靠性和高性能的数据持久化方式。除非部署MQ机器本身或是本地磁盘挂了,否则一般是不会出现无法持久化的故障问题。并且对文件系统进行顺序的写入效率很高.
性能对比:
文件系统> 关系型数据库DB
消息的存储和发送
1. 消息存储
磁盘如果使用得当,磁盘的速度完全可以匹配上网络 的数据传输速度。目前的高性能磁盘,顺序写速度可以达到600MB/s, 超过了一般网卡的传输速度。但是磁盘随机写的速度只有大概100KB/s,和顺序写的性能相差6000倍!因为有如此巨大的速度差别,好的消息队列系统会比普通的消息队列系统速度快多个数量级。RocketMQ的消息用顺序写,保证了消息存储的速度。
2. 消息发送
Linux操作系统分为【用户态】和【内核态】,文件操作、网络操作需要涉及这两种形态的切换,免不了进行数据复制。
一台服务器 把本地磁盘的文件内容发送到客户端,一般分为两个步骤:
-
read: 读取本地文件到用户态的内存
-
write: 将读取的内容通过网络发送出去
两个操作,实际进行了四次的数据复制
- 从磁盘将数据复制到内核态内存
- 从内核态内存将数据复制到用户态内存
- 然后从用户态内存复制到网络驱动的内核态内存
- 最后从内核态内存复制到网卡中进行传输
而零拷贝技术可以省去向用户态复制的操作,直接内核态将数据从磁盘读出来之后,直接发送出去,不在向用户态进行复制,这种方式的前提是数据不需要修改,而消息队列的场景一般不需要对消息进行修改.
零拷贝有两种方式:
-
mmap+write
优点:即使频繁调用,使用小块文件传输,效率也很高
缺点:不能很好的利用 DMA 方式,会比 sendfile 多消耗 CPU,内存安全性控制复杂,需要避免 JVM Crash
问题。
-
sendfile
优点:可以利用 DMA 方式,消耗 CPU 较少,大块文件传输效率高,无内存安全新问题。
缺点:小块文件效率低于 mmap 方式,只能是 BIO 方式传输,不能使用 NIO。
Rocket选择的是第一种方式来提高消息存盘和网络发送的速度。
这种机制在java中是通过MappedByteBuffer实现的.
这里需要注意的是,采用MappedByteBuffer这种内存映射的方式有几个限制,其中之一是一次只能映射1.5~2G 的文件至用户态的虚拟内存,这也是为何RocketMQ默认设置单个CommitLog日志数据文件为1G的原因了
文件系统
RocketMQ 选择 Linux Ext4 文件系统,原因如下:
Ext4 文件系统删除 1G 大小的文件通常耗时小于 50ms,而 Ext3 文件系统耗时约 1s 左右,且删除文件时,磁盘IO 压力极大,会导致 IO 写入超时。
文件系统 IO 调度算法需要调整为 deadline,因为 deadline 算法在随机读情况下,可以合并读请求为顺序跳跃方式,从而提高读 IO 吞吐量。
数据存储结构
RocketMQ消息的存储是由ConsumeQueue和CommitLog配合完成的,除此之外还有一个indexFile来提供根据key或者时间分区来查询消息的 方法.
CommitLog
CommitLog是消息真正的物理存储文件,并且一个Broker中的所有消息不区分topic均存储在一个CommitLog中.CommitLog是完全的顺序写,随机读,每个CommitLog文件的大小为1G,超过了则新建一个新的CommitLog,CommitLog文件名按照偏移量顺序命名.
ConsumeQueue
ConsumeQueue是消息的逻辑队列,类似数据库的索引文件,存储的是消息在CommitLog的索引。每 个Topic下的每个Message Queue都有一个对应的ConsumeQueue文件。
IndexFile
为了消息查询提供了一种通过key或时间区间来查询消息的方法,这种通过IndexFile来查找消息的方法不影响发送与消费消息的主流程
消费模式
1. 集群模式
集群模式就是group模式,采用负载均衡的方式,每条消息都从每个group中选择一个消费者消息
2. 广播模式
每个group中的每个消费者都会收到同一个消息
负载均衡
发送负载均衡
Producer端,每个实例在发消息的时候,默认会轮询所有的message queue发送,以达到让消息平均落在不同的queue上。而由于queue可以散落在不同的broker,所以消息就发送到不同的broker下,如下图:
图中箭头线条上的标号代表顺序,发布方会把第一条消息发送至 Queue 0,然后第二条消息发送至 Queue 1,以此类推。
另外也可以自定义方式选择发往哪个队列。