从基础功能拆解RabbitMQ的架构设计与实现

367 阅读4分钟

RabbitMQ 系统架构

image.png

RabbitMQ 由 Producer、Broker、Consumer 三个大模块组成。生产者将数据发送到 Broker,Broker 接收到数据后,将数据存储到对应的 Queue 里面,消费者从不同的 Queue 消费数据。

Exchange 称为交换器,它是一个逻辑上的概念,用来做分发,本身不存储数据。流程上生产者先将消息发送到 Exchange,而不是发送到数据的实际存储单元 Queue 里面。然后 Exchange 会根据一定的规则将数据分发到实际的 Queue 里面存储。

这个分发过程就是 Route(路由),设置路由规则的过程就是 Bind(绑定)。即 Exchange 会接收客户端发送过来的 route_key,然后根据不同的路由规则,将数据发送到不同的 Queue 里面。

协议和网络模块

RabbitMQ 在协议内容和连接管理方面,都是遵循 AMQP 规范。即 RabbitMQ 的模型架构和 AMQP 的模型架构是一样的,交换器、交换器类型、队列、绑定、路由键等都是遵循 AMQP 协议中相应的概念。

AMQP 是一个应用层的通信协议,可以看作一系列结构化命令的集合,用来填充 TCP 层协议的 body 部分。

image.png

在 RabbitMQ 的网络层有 Connectoion 和 Channel 两个概念需要关注。

image.png

Connection 是指 TCP 连接,Channel 是 Connection 中的虚拟连接。两者的关系是:一个客户端和一个 Broker 之间只会建立一条 TCP 连接,就是指 Connection。Channel(虚拟连接)的概念在这个连接中定义,一个 Connection 中可以创建多个 Channel。

客户端和服务端的实际通信都是在 Channel 维度通信的。

数据存储

RabbitMQ 的存储模块也包含元数据存储与消息数据存储两部分,RabbitMQ 的两类数据都是存储在 Broker 节点上的。

元数据存储

RabbitMQ 的元数据都是存在于 Erlang 自带的分布式数据库 Mnesia 中的。

Mnesia 对 RabbitMQ 的作用,相当于 ZooKeeper 对于 Kafka、NameServer 对于 RocketMQ 的作用。

消息数据存储

RabbitMQ 消息数据的最小存储单元是 Queue,即消息数据是按顺序写入存储到 Queue 里面的。在底层的数据存储方面,所有的 Queue 数据是存储在同一个“文件”里面的。这个“文件”是一个虚拟的概念,表示所有的 Queue 数据是存储在一起的意思。

image.png

这个“文件”由队列索引(rabbit_queue_index)和消息存储(rabbitmq_msg_store)两部分组成。

rabbit_msg_store 是一个逻辑概念,底层的实际存储单元分为两个,msg_store_persistent 和 msg_store_transient,分别负责持久化消息和非持久化消息的存储。

生产者和消费者

当生产者和消费者连接到 Broker 进行生产消费的时候,是直接和 Broker 交互的,不需要客户端寻址。

image.png

生产端发送数据不是直接发送到 Queue,而是直接发送到 Exchange。即发送时需要指定 Exchange 和 route_key,服务端会根据这两个信息,将消息数据分发到具体的 Queue。因为 Exchange 和 route_key 都是一个逻辑概念,数据是直接发送到 Broker 的,然后在服务端根据路由绑定规则,将数据分发到不同的 Queue 中,所以在客户端是没有发送生产分区分配策略的逻辑。其实从某种程度来看,Exchagne 和 Route 的功能就是生产分区分配的过程,只是将这个逻辑从客户端移动到了服务端而已。

在消费端,RabbitMQ 支持 Push(推)和 Pull(拉)两种模式,如果使用了 Push 模式,Broker 会不断地推送消息给消费者。不需要客户端主动来拉,只要服务端有消息就会将数据推给客户端。当然推送消息的个数会受到 channel.basicQos 的限制,不能无限推送,在消费端会设置一个缓冲区来缓冲这些消息。拉模式是指客户端不断地去服务端拉取消息,RabbitMQ 的拉模式只支持拉取单条消息。

消费者是直接消费 Queue 数据的。

总结 RabbitMQ

可以从以下七个方面入手:

  1. 协议层基于 AMQP 标准开发
  2. 。网络层核心数据流基于 TCP 协议通信,并通过 Connection 和 Channel 机制实现连接的复用,以减少创建的 TCP 连接数量。
  3. 存储层基于多个 Queue 数据统一到一个文件存储的思路设计,同时支持分段存储和基于时间的数据过期机制。
  4. 元数据存储是基于 Erlang 内置的数据库 Mnesia 来实现。
  5. 客户端的访问是直连的,没有客户端寻址机制。
  6. 生产端是通过 Exchange 和 Route 写入数据的,生产数据的分发是在服务端完成的,其他消息队列的分发一般都是在客户端。
  7. 消费端没有消费分组、消费分区分配等概念,直连 Queue 消费,同时也提供了手动和自动两种 ACK 机制。

此文章为11月Day10学习笔记,内容来源于极客时间《深入拆解消息队列 47 讲》