RabbitMQ 简介
#1. 简介
RabbitMQ 是一个由 Erlang 语言开发的基于 AMQP 标准的开源实现,同时它也是最常见的 AMQP 实现。
对于 Broker、Producer、Consumer 等基本概念,AMQP 标准和 JMS 标准是一样的。
不同的是 AMQP 规范相较于 JMS 规范『多』了 Exchange 和 Binding 两个概念,这两个概念组成了 AMQP 规范的特色功能:路由。
#2. 路由
消息的生产者需要把消息发布到 Exchange 上,消息最终到达队列并被消费者接收,而 Binding 决定交换器上的消息应该被发送到哪个队列中。
不同类型的交换器分发消息(至队列)的策略也不同,目前交换器有 4 种类型,除 Headers 类型功能有重复且性能较差,需要了解掌握的有:Direct、Fanout、Topic 。
类型 | 说明 |
---|---|
Direct | 其类型的行为是 先匹配、再投送 ,即在绑定时设定一个 routing_key ,消息的 routing_key 与之匹配时,才会被交换器投送到所绑定的队列中去。 |
Topic | 按规则转发消息(最灵活)。 |
Headers | 设置 header attribute 参数类型的交换机。 |
Fanout | 转发消息到所有绑定队列。 |
#Direct 交换器
Direct 类型是 RabbitMQ 默认的交换机类型(也是最简单的模式):根据 key 全文匹配去寻找队列。
以上图为例:
-
X 和 Q1 的 binding key 是
orange
; -
X 和 Q2 有 2 个 binding key,分别是
black
和green
。 -
当 P 发送至 X 的消息中的路由键与这 3 个 binding-key 中的某一个对应上时,那么消息就被路由器『转交』到了对应的队列(交给了
队列1
,从而消费者1
收到了消息)。
如果消息种的路由键(routing key)和 Binding 中的绑定键(binding key)一致,交换器就将消息发送到对应的队列中。路由键与队列名称要完全匹配。
相当于 SQL 中的 =
规则。
#Topic 交换器
Topic 类型交换机相当于是 Direct 类型的升级版:它允许使用通配符。
Topic 类型的交换机对于 Binding Key 的设置有一定的要求:
以上图为例:
- P 发送到 X 的消息的 binding-key 符合
xxx.orange.xxx
规则,那么,该消息会被 X 转交给 Q1 。 - P 发送到 X 的消息的 binding-key 符合
xxx.xxx.rabbit
规则,那么,该消息会被 X 转交给 Q2 。 - P 发送到 X 的消息的 binding-key 符合
lazy.xxx.xxx.xxx....
规则,那么,该消息会被 X 转交给 Q2 。
#Fanout 交换器
Fanout 就是消息广播,完全不考虑 key 的情况,交换器 X 将它收到的所有消息发送给所有与之绑定的消息队列(无一例外)。
Fantout 交换器不处理路由键(有也当作没看见),只是简单地将队列绑定到交换器,发送到交换器的每条消息都会被转发到与该交换器绑定的所有队列中。
相当于子网广播,子网内的每个主机都获得了一份复制的消息。
通过 Fanout 交换器转发消息是最快的。
#Headers 交换器
Headers 类型交换器是早期的一种交换器,其工作机制与上述三者完全不一样,而且性能最低。所以现在基本不再使用。
#3. 默认交换机
默认交换机,又名 Default 交换机。它是一个没有名字的、Direct 类型的交换机。
在 RabbitMQ 中:
-
每个队列都要和一个交换机有绑定关系
如果你没有指明、配置队列与哪个交换机有绑定关系,那么它(队列)默认就是与 Default Exchange 有绑定关系,并且是以 queue-name 作为 banding-key 。
-
每一个发往 RabbitMQ 的消息,都要说明是发送个 RabbitMQ 上的哪个交换机。
如果你在向 RabbitMQ 发送消息时没有指明、配置时发往哪个交换机,那么它(消息)默认就是发往了 Default Exchange 。
#3. 虚拟主机(了解、自学)
虚拟主机(Virtual Host,在 RabbitMQ 中称作 vhost)是 AMQP 规范中的一个基本概念,客户端在连接消息服务器时必须指定一个虚拟主机。
虚拟主机本质上就是一个缩小版的 RabbitMQ 服务器,其内部有自己的队列、交换器、绑定等。
RabbitMQ
│
│── 虚拟主机-1
│ │── Queue
│ │── Exchange
│ └── Binding
│
│── 虚拟主机-2
│ │── Queue
│ │── Exchange
│ └── Binding
│
└── 虚拟主机-3
│── Queue
│── Exchange
└── Binding
Copied!
比较特别的是,RabbitMQ 中的权限控制是以 vhost 为单位的。也就是说,消息客户端在访问时不能把 vhost-A 中的 Exchange 绑定到 vhost-B 中的 Queue 上。
如果一个 RabbitMQ 服务器被多个应用共用,此时就可以让每一个应用使用一个 vhost,而不用担心相互之间的干扰。
RabbitMQ 中有一个默认的 vhost,它的名字/值是 /
,用户名和密码都是 guest
。
RabbitMQ 提供了 rabbitmqclt
工具管理 vhost:
## 创建虚拟主机 test
rabbitmqctl add_vhost test
## 删除虚拟主机 test
rabbitmqctl delete_vhost test
## 查询当前 RabbitMQ 中所有的虚拟主机
rabbitmqctl list_vhosts
Copied!
#4. Connection 和 Channel(了解、自学)
无论是生产者还是消费者,都需要和 RabbitMQ Broker 建立连接,这个连接就是一条 TCP 连接,也就是 Connection 。一旦 TCP 连接建立起来,客户端紧接着就可以创建一个 AMQP 信道(Channel),每个信道都会被指派一个唯一的 ID 。
信道是建立再 Connection 之上的虚拟连接,RabbitMQ 处理的每条 AMQP 指令都是通过信道完成的。
我们完全可以直接使用 Conneciton 就能完成信道的工作,为什么还要引入信道呢?
试想这样的一个场景,一个应用程序中有很多个线程需要从 RabbitMQ 中消费消息,或生产消息,那么必然需要建立很多个 Connection,也就是多个 TCP 连接。然而对于操作系统而言,建立和销毁 TCP 连接时有开销的。如果遇到使用高峰,性能瓶颈也随之显现。
RabbitMQ 采用类似 NIO(Non-block I/O)的做饭,选择 TCP 连接复用,不仅可以减少性能开销,同时也便于管理。
NIO,也称非阻塞 I/O,包括三大核心部分:Channel(信道)、Buffer(缓存区)和 Selector(选择器)。NIO 基于 Channel 和 Buffer 进行操作,数据总是从信道读取数据到缓冲区,或者从缓冲区中写入信道中。Selector 用于监听多个信道的时间(比如链接打开,数据到达等)。因此,单线程可以监听多个数据的信道。
每个线程把持一个信道,所以信道复用了 Connection 的 TCP 连接。同时 RabbitMQ 可以确保每个线程的私密性,就像拥有独立的连接一样。当每个信道的流量不是很大时,复用单一的 Connection 可以有效地节省 TCP 连接资源。
信道在 AMQP 中是一个很重要的概念,大多数操作都是在信道这个层面展开的。