MQ相关介绍
消息队列是一种可以实现系统异步通信的中间件,常用于解决系统异步解耦和请求(TPS)削峰填谷
的问题。即它面向的是开发人员,而非终端用户可以直接使用的产品;
三种消息协议
JMS (Java Message Service)
- JMS 本质上是 JAVA API。在 JMS 中定义了 Producer,Consumer,Provider 三种角色,Producer 作为消息的发送方,Consumer 作为消息的接收方,Provider 作为服务的提供者,Producer 和 Consumer 统称为 Client。
- JMS 定义了
点对点和发布订阅两种消息模型
,发布订阅模型中,通过 topic 对消息进行路由,生产者可以将消息发到指定的 topic,消费者订阅这个 topic 即可收到生产者发送的消息。 - 一个生产者可以向一个或多个 topic 中发送消息,一个消费者也可以消费一个或多个 topic 中的消息,一个 topic 也可以有多个生产者或消费者,生产者和消费者只需要关联 topic,而不用关心这消息由谁发送或者消费。 Provider 为每一个 topic 维护一个或多个 queue 来保存消息,消息在 queue 中是有序的,遵循先进先出的原则,不同 queue 间的消息是无序的。
- 点对点模式中没有 topic 的概念,生产者直接将消息发送到指定 queue,消费者也指定 queue 进行消费,消息只能被一个消费者消费,不可以被多个消费者消费。
Kafka 和 RocketMQ 都实现了或部分实现了 JMS 协议。
AMQP(Advanced Message Quequing Protocol)[高级消息队列协议]
与 JMS 不同,
AMQP 是一个应用层的网络传输协议,对报文格式进行定义,与开发语言无关
。在 AMQP 中同样有生产者,消费者两种角色,消息也是保存在 queue 中的。 但不同于 JMS 用 topic 对消息进行路由,AMQP 的路由方式由 exchange 和 binding 决定。client 可以创建 queue,并在创建 queue 的同时通知 exchange 这个 queue 接受符合什么条件的消息,这个条件即为 Bingding key。生产者发送消息到 exchange 的时候会指定一个 router key,exchange 收到消息后会与自己所维护的 Bingding key 做比较,发送到符合条件的 queue 中。消费者在消费时指定 queue 进行消费。
RabbitMQ 实现了 AMQP 协议。
MQTT(Message Queuing Telemetry Transport)
MQTT 协议是一种基于发布订阅的轻量级协议,支持 TCP 和 UDP 两种连接方式,主要应用于即时通讯,小型设备,移动应用等领域。 MQTT 中有发布者(Publish),订阅者(Subscribe)和代理服务器(Broker)三种角色。Broker 是服务的提供者,发布者和前两种协议中的生产者相同,将消息(Message)发送到 Broker,Subscribe 从 Broker 中获取消息并做业务处理。
MQTT 的 Message 中固定消息头(Fixed header)仅有 2 字节,开销极小,除此之外分为可变头(Variable header)和消息体(payload)两部分。固定头中包含消息类型,消息级别,变长头的大小以及消息体的总长度等信息。 变长头则根据消息类别,含有不同的标识信息。 MQTT 允许客户端动态的创建主题,发布者与服务端建立会话(session)后,可以通过 Publish 方法发送数据到服务端的对应主题,订阅者通过 Subscribe 订阅主题后,服务端就会将主题中的消息推送给对应的订阅者。
RocketMq-架构组件
NameServer
NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步,他们之间是独立的、并行的,各自保留了一份全部的 Broker、Topic 等集群信息。
- 非常简单的 Topic 路由注册中心;
- 支持 Broker 的动态注册与发现;
- Broker 心跳检测;
- 为 Producer、Consumer 集群提供 Topic 路由功能(维护Topic跟Broker的映射关系);
BrokerServer
Broker主要负责消息的存储、投递和查询以及服务高可用保证,包含四个主要的模块。
- Remoting Module:整个Broker的实体,负责处理来自clients端的请求;
- Client Manager:负责管理客户端(Producer/Consumer)和维护Consumer的Topic订阅信息;
- Store Service:提供方便简单的API接口处理消息存储到物理硬盘和查询功能;
- HA Service:高可用服务,提供Master Broker 和 Slave Broker之间的数据同步功能;
- Index Service:根据特定的Message key对投递到Broker的消息进行索引服务,以提供消息的快速查询;
Producer
消息发布的角色,支持分布式集群方式部署,无状态信息。
Consumer
消息消费的角色,支持分布式集群方式部署。支持以push推,pull拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制。
网络部署特点
---**-
- NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。
- Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master。
BrokerId为0表示Master,非0表示Slave
。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。 - Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic 服务的 Broker 节点中的Master建立长连接,
且定时向Master发送心跳
。Producer完全无状态,可集群部署。 - Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,
并向提供Topic服务的Broker 节点中的Master、Slave建立长连接,且定时向Master、Slave发送心跳
。
连接关系
- NameServer 是一个集群;
Broker 是一个集群,分为主节点和从节点,主节点可以读写,从节点只能读取消息,并定时向所有的 NameServer 发送心跳信息,定时注册 Topic 信息到 NameServer 中
;- Producer 是一个集群
- Consumer 是一个集群
- Producer 随机建立一个长连接 NameServer,从中获取 Topic 信息,并与 Topic 所在 Master 建立长连接、发送消息、定时发送心跳信息。
- Consumer 随机长连接一个 NameServer,从中获取 Topic 信息,并与 Topic 所在 Master 或 Slaver 建立长连接、发送消息、定时发送心跳信息。
工作流程
- 启动NameServer,NameServer起来后监听端口,等待Broker、Producer、Consumer连上来,相当于一个路由控制中心;
Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有Topic信息
。注册成功后,NameServer集群中就有Topic跟Broker的映射关系;- 收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic;
- Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前发送的Topic存在哪些Broker[Master]上,轮询从队列列表中选择一个队列,然后与队列所在的Broker建立长连接从而向Broker发消息;
- Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息;
消息存储整体架构
- CommitLog:消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容不是定长的。
消息主要是顺序写入日志文件,当文件满了,写入下一个文件;
单个文件大小默认1G ,文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。
- ConsumeQueue:消息消费队列,引入的目的主要是提高消息消费的性能,由于RocketMQ是基于主题topic的订阅模式,消息消费是针对主题进行的,如果要遍历commitlog文件中根据topic检索消息是非常低效的。
- Consumer即可根据ConsumeQueue来查找待消费的消息。其中,ConsumeQueue(逻辑消费队列)作为消费消息的索引,保存了指定Topic下的队列消息在CommitLog中的起始物理偏移量offset,消息大小size和消息Tag的HashCode值。
consumequeue文件可以看成是基于topic的commitlog索引文件,故consumequeue文件夹的组织方式如下:topic/queue/file三层组织结构,具体存储路径为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}。同样consumequeue文件采取定长设计,每一个条目共20个字节,分别为8字节的commitlog物理偏移量、4字节的消息长度、8字节tag hashcode,单个文件由30W个条目组成,可以像数组一样随机访问每一个条目,每个ConsumeQueue文件大小约5.72M;
- IndexFile:IndexFile(索引文件)提供了一种可以通过key或时间区间来查询消息的方法。Index文件的存储位置是:$HOME/store/index/{fileName},文件名fileName是以创建时的时间戳命名的,固定的单个IndexFile文件大小约为400M,一个IndexFile可以保存 2000W个索引,
IndexFile的底层存储设计为在文件系统中实现HashMap结构,故rocketmq的索引文件其底层实现为hash索引
;
- 在上面的RocketMQ的消息存储整体架构图中可以看出,RocketMQ采用的是混合型的存储结构,即为Broker单个实例下所有的队列共用一个日志数据文件(即为CommitLog)来存储;
- RocketMQ的混合型存储结构(多个Topic的消息实体内容都存储于一个CommitLog中)针对Producer和Consumer分别采用了数据和索引部分相分离的存储结构,Producer发送消息至Broker端,然后Broker端使用同步或者异步的方式对消息刷盘持久化,保存至CommitLog中;
- 只要消息被刷盘持久化至磁盘文件CommitLog中,那么Producer发送的消息就不会丢失。正因为如此,Consumer也就肯定有机会去消费这条消息。
当无法拉取到消息后,可以等下一次消息拉取,同时服务端也支持长轮询模式,如果一个消息拉取请求未拉取到消息,Broker允许等待30s的时间,只要这段时间内有新消息到达,将直接返回给消费端
。- 这里,RocketMQ的具体做法是,使用Broker端的后台服务线程—ReputMessageService不停地分发请求并异步构建ConsumeQueue(逻辑消费队列)和IndexFile(索引文件)数据。
- Consumer消费是以"Topic"为粒度的,但是CommitLog是所有Topic消息的汇总存储,这时候需要一个以Topic为维度的commitLog文件offset的索引,便于消费这个Topic 下的数据,因此产生了 ConsumeQueue。相当于一个Topic下,有多个MessageQueue消息队列,然后将消息队列映射为ConsumeQueue消息消费队列,供Consumer快捷消费这个Topic下的数据。
RocketMQ专业术语
Producer
消息生产者,位于用户的进程内,Producer通过NameServer获取所有Broker的路由信息
,根据负载均衡策略选择将消息发到哪个Broker,然后调用Broker接口提交消息。
Producer Group
生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。
Consumer
消息消费者,位于用户进程内。Consumer通过NameServer获取所有broker的路由信息后,向Broker发送Pull请求来获取消息数据。Consumer可以以两种模式启动,广播(Broadcast)和集群(Cluster),广播模式下,一条消息会发送给所有Consumer,集群模式下消息只会发送给一个Consumer。
Consumer Group
消费者组,和生产者类似,消费同一类消息的多个 Consumer 实例组成一个消费者组。
Topic
Topic用于将消息按主题做划分,Producer将消息发往指定的Topic,Consumer订阅该Topic就可以收到这条消息。Topic跟发送方和消费方都没有强关联关系,发送方可以同时往多个Topic投放消息,消费方也可以订阅多个Topic的消息。在RocketMQ中,Topic是一个上逻辑概念。消息存储不会按Topic分开。
Message
代表一条消息,使用MessageId
唯一识别,用户在发送时可以设置messageKey,便于之后查询和跟踪。一个 Message 必须指定 Topic,相当于寄信的地址。Message 还有一个可选的 Tag 设置,以便消费端可以基于 Tag 进行过滤消息。也可以添加额外的键值对,例如你需要一个业务 key 来查找 Broker 上的消息,方便在开发过程中诊断问题。
Tag
标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。
Broker
Broker是RocketMQ的核心模块,负责接收并存储消息
,同时提供Push/Pull接口来将消息发送给Consumer。Consumer可选择从Master或者Slave读取数据。多个主/从组成Broker集群,集群内的Master节点之间不做数据交互。Broker同时提供消息查询的功能,可以通过MessageID和MessageKey来查询消息。Borker会将自己的Topic配置信息实时同步到NameServer。
Queue
Topic和Queue是1对多的关系,一个Topic下可以包含多个Queue,主要用于负载均衡。发送消息时,用户只指定Topic,Producer会根据Topic的路由信息选择具体发到哪个Queue上。Consumer订阅消息时,会根据负载均衡策略决定订阅哪些Queue的消息。
Offset
RocketMQ在存储消息时会为每个Topic下的每个Queue生成一个消息的索引文件,每个Queue都对应一个Offset记录当前Queue中消息条数。
NameServer
NameServer可以看作是RocketMQ的注册中心,它管理两部分数据:集群的Topic-Queue的路由配置;Broker的实时配置信息。其它模块通过Nameserv提供的接口获取最新的Topic配置和路由信息。
Producer/Consumer
:通过查询接口获取Topic对应的Broker的地址信息Broker
: 注册配置信息到NameServer, 实时更新Topic信息到NameServer