下面是几个要用到的名词解释,我们来了解一下。
1、代理服务 Broker
我们知道如果要使用 RabbitMQ,必须先要安装一个 RabbitMQ 服务。这个服务就是 Broker,中文叫做代理,因为 MQ 服务器帮我们对消息做了存储和转发。一般情况下为了保证服务的高可用,需要多个 Broker。
2、连接 Connection
无论是生产者还是消费者,都需要与 Broker 建立连接,这个连接就是 Connection ,它是一个 TCP 的长连接。
3、信道 Channel
为了节省服务器资源,避免建立太多的长连接,我们需要复用连接。于是就有了 Channel 的概念。我们可以在保持的 TCP 长连接里面去创建和释放 Channel,从而减少资源的消耗。其中 Channel 是相互隔离的,不能共享。
信道是生产者消费者与 RabbitMQ 通信的渠道,信道是建立在 TCP 连接上的虚拟连接,就是说 RabbitMQ 在一条 TCP 上建立成百上千个信道来达到多线程处理,这个 TCP 被多个线程共享,每个线程对应一个通道,信道在 RabbitMQ 都有唯一的 id ,保证了信道私有性,对应上唯一的线程使用。
为什么不建立多个 TCP 连接呢?
RabbitMQ 是为了保证性能,系统为每个线程开辟一个 TCP 连接是非常消耗性能的,每秒成百上千的建立和销毁 TCP 会严重消耗系统,TCP 的创建和销毁,开销大,创建需要三次握手,销毁需要四次挥手,所以 RabbitMQ 选择建立多个信道连接到 RabbitMQ 上。
就类似于 TCP 是电缆,信道就是里面的光纤,每个光纤都是独立的互补影响。
4、队列 Queue
连接到 Broker 以后,就可以收发消息了。在 Broker 上有一个对象用来存储消息,在 RabbitMQ 里就是 Queue。实际上消息是存储在数据库里的,叫做 Mnesia,它是一个分布式数据库管理系统 (DBMS)。
Queue 是生产者与消费者的纽带,生产者发送的消息到达队列,在队列中存储,消费者从队列中消费消息。
5、消费者 Consumer
Consumer 是消费者,消费者如何消费消息呢?是主动从 Broker 上拉取呢?还是被动等 Broker 推过来呢?
RabbitMQ 实现了这两种方式:
- ① 一种是 Pull 模式,即每隔一段时间获取一次消息,消息的实时性会有所降低,但同时消费的主动权在消费端,消费端可以根据自身情况决定什么时候去 Pull。
- ② 另一种方式 Push 模式,即生产者发送消息后,Broker 就马上推送给消费者,消息保存在客户端,实时性要求高,但是如果消费端消费不过来就会造成消息积压。
我们想象一下如果生产者的消息会发送到多个队列,这时候如果使用 push 的方式是不是需要多次 push,这样无疑增加了生产者的成本。那如何处理呢?于是就又了 Exchange。
6、交换机 Exchange
通过 Exchange 不管有多少个队列需要接收消息,我们都只需发送到 Exchange 就好了,由它来根据具体的绑定规则分发到具体的队列。
Exchange 的种类:
-
① direct:
直连交换机,顾名思义就是从 交换机-路由键-队列 三者进行绑定,然后获取准确的队列信息。一个队列与直连交换机绑定,需要指定一个明确的绑定键 (binding key)。生产者发送消息时会携带一个路由键 (routing key),当消息的路由键与某个队列的绑定键完全匹配时,这条消息才会从交换机路由到这个队列上。
使用场景:直连类型的交换机适用于一些业务用途明确的消息,使用明确的绑定键,就可以创建一个直连类型的交换机。
-
② fanout:
广播类型的交换机与队列绑定时不需要指定绑定键。因此生产者发送消息到广播类型的交换机上,也不需要携带路由键。消息到达交换机时,fanout 类型交换机会将接收到的消息广播给所有与之绑定的队列,即所有与之绑定的队列都会收到相同的消息,这也就和路由键没有关系了。
使用场景:广播类型的消息适用于一些通用的业务消息。比如用户登录了,需要监听用户登录的系统自己监听就好了。
-
③ topic:
主题交换机,用于匹配一个或多个路由键的,可以用 * 代替。即一个队列与 topic 类型的交换机绑定时,可以在绑定键中使用通配符。具体语法类似于正则匹配,符合匹配规则的则发送到队列,否则不发送。
使用场景:主题类型的交换机适用于一些根据业务主体或者消息等级过滤消息的场景,比如一个订单状态,有的关注订单下单状态,有的关注订单派单状态,有的关注订单完成状态等。下游的业务系统根据自己关心的状态,设置不同的绑定键去接收消息就好了。
-
④ header(非路由键匹配,功能和 direct 类似,很少用)。
将消息中的 headers 与该 Exchange 相关联的所有 Binging 中的参数进行匹配,如果匹配上了,则发送到该 Binding 对应的 Queue 中。性能方面比后者差很多,所以在实际项目中用的很少。
前三种类似集合对应关系:(direct)1:1,(fanout)1:N,(topic)N:1
交换器本质是一张路由查询表(名称和队列 id,类似于 hash 表),这是一个虚拟出来的东西,并不存在真实的交换器。
7、vhost
如果我们只搭建了一套 RabbitMQ 机器,有多个用户想使用。首先在一个 Broker 上大家规范好自己的 Exchange、Queue、RoutingKey 。不过这样会很难维护,因为你不知道别人有没有用过这个 Queue 和 RoutingKey 。
那该怎么办呢?再搭建一套服务器?
显然不太好,所以就有了 vhost 的概念,可以理解为虚拟主机、虚拟空间。其实就是通过 vhost 将环境做到了隔离。除此之外还可以提高硬件资源的利用率。RabbitMQ 有一个默认的 vhost,就是 “/”。
8、RabbitMQ的三个组件
- ① 交换机(Exchange)
-
② 队列(Queue):用来存储消息的数据结构,位于硬盘或内存中。
负责存储接收到的消息,同时也可能包含如何处理消息的配置信息。队列可以把消息只存储在内存中,也可以存储在磁盘中,然后以先进先出(FIFO)的顺序进行投递。
-
③ 绑定(Binding):一套规则,用于告诉交换器消息应该被存储到哪个队列。
AMQ 模型使用绑定(binding)来定义队列和交换器之间的关系
9、总结
- 信道才是 RabbitMQ 的通信本质,生产者和消费者都是通过信道完成消息生产和消费的。
- 交换器本质是一张路由查询表(名称和队列 id,类似于 hash 表),这是一个虚拟出来的东西,并不存在真实的交换器。
- 消息的生命周期:生产者生产消息 A 交由信道,信道通过消息(消息有载体和标签)的标签(路由键)放到交换器发送到队列上(其实就是查询匹配,一旦匹配到了规则,信道就直接和队列产生连接,然后将消息发送过去)。
- Channel 是我们与 RabbitMQ 打交道的最重要的一个接口,我们大部分的业务操作是在Channel 这个接口中完成的,包括定义 Queue、定义 Exchange、绑定 Queue 与 Exchange、发布消息等。