1. RabbitMQ是什么东西

RabbitMQ是采用 Erlang 语言开发的 AMQP(Advanced Message Queuing Protocol,高级消息队例协议)的开源实现。它最初起源于金融系统,用于在分布式系统中存储转发消息。

RabbitMQ的具体特点可以概括为以下几点:

  • 可靠性:RabbitMQ 使用一些机制来保证可靠性,如持久化传输确认发布确认等。
  • 灵活的路由:在消息进入队列之前,通过Exchange来路由消息。
  • 扩展性:多个MQ节点可以组成一个集群,可以根据根据实际业务情况动态扩展集群。
  • 高可用性:队列可以在集群机器上设置镜像,使得部分节点出现问题时队列仍然可用。
  • 多种协议:RabbitMQ 支持多种消息队列协议,除了AMQP外,还支持STOMP、MQTT等。
  • 多语言客户端:RabbitMQ 几乎支持所有常用语言,比如 php、Python、C等;
  • 管理界面:RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 、节点等。(Web管理界面也是一种插件,还可以通过HTTP API进行管理)
  • 插件机制:RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。

2. RabbitMQ相关概念

2.1 基本概念

RabbitMQ整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。
RabbitMQ模型架构如下图:

上图中涉及的概念主要有:

(1)消息(Message)

消息一般可以包含2个部分:消息体消息属性消息体(也成payload)一般是带有业务逻辑结构的数据,比如Json字符串;消息属性用于表述消息,比如消息对应的Exchange名称和RoutingKey。

(2)生产者(Producer)

生产者就是发送消息的一方。

  • 个人理解本质上就是一个channel,channel执行了 basicPublish 的API。

(3)消费者(Consumer)

消费者就是接收消息的一方。

  • 个人理解本质上就是一个channel,channel执行了basicConsume的API。

(4)队列(Queue)

队列在RabbitMQ中用于存储消息。生产者所发送的消息最终被投递到队列中,消费者从队列获取消息并消费。

  • RabbitMQ中消息只存储在队列中,Exchange只做路由、不存储消息。
  • 所有队列都绑定到默认交换机exchange="",并且绑定时使用的routingKey=queueName
 

(5)交换机(Exchange)

生产者将消息发送给Exchange,Exchange再将消息路由给队列。

  • 消息的路径是:生产者 -> 交换机 -> 队列

(6)路由键(RoutingKey)

生产者在发送消息时,需要指定Exchange和RoutingKey,并且队列和Exchange之间的绑定关系也通过RoutingKey来标识。

(7)绑定(Binding)

绑定可以理解为一个三元组<queueName, exchangeName, bindingKey>,表示将queue用bindingKey绑定到exchange,该exchange收到的消息时,会根据exchange类型和消息的routingKey将消息路由到符合的队列。

  • 也可以将交换机绑定到交换机。

(8)节点(Broker)

节点是一个RabbitMQ服务器实例,一个集群中有多个节点,这些节点可以在多台机器上,也可以在同一台机器上(端口不同)。

(9)虚拟主机(Virtual Host)

虚拟主机是共享相同身份认证和加密环境的独立服务器域。不同虚拟主机之间是相互隔离的,拥有独立的交换机、队列、绑定等。

  • 可以将虚拟主机与节点之间的关系理解为数据库与mysql之间的关系,一台mysql服务上有不同的数据库。

(10)连接(Connection)

Connection是客户端与服务器之间建立的一个网络连接。

(11)信道(Channel)

Channel可以理解为一种轻量连接,多个channel共享一个Connection。
由于创建和销毁Connection的开销大,所以在Connection内部建立Channel。

  • 不同的Channel之间互相隔离
  • Channel是双向的数据通道。
  • RabbitMQ几乎所有操作均是通过Channel完成,包括发送消息、创建队列、交换机等、消费消息。先创建Connection、然后创建Channel,再使用Channel执行操作。

2.2 Exchange

2.2.1 交换机参数

以php Channel接口API来说明创建交换机的主要参数。

channel.exchangeDeclare(exchange, type);.
// 第一个参数,exchange:交换机名称。数据类型:String
// 第二个参数,type:交换机的类型(direct/topic/fanout)。数据类型:String

2.2.2 交换机类型

RabbitMQ常用交换机类型有fanoutdirecttopic四种。

2.2.2.1 广播模式(fanout)

当一个交换机是广播模式的话,所有绑定到该交换机上的队列(或交换机)都能收到路由的消息,不论bindingKey的取值。

  • 队列而言,无论队列与交换机绑定时bindingkey取值是什么、也不论生产者发送消息时的routingkey是什么,该队列都可以收到广播交换机上所有消息。
  • 对于绑定到广播交换机上的交换机而言,同样如此。
    假设Exchange1是广播交换机,Exchange2是direct交换机,队列Queue1、Queue2绑定到Exchange2交换机。那么Exchange2可以收到Exchange1上所有消息,但是队列Queue1和Queue2只能收到routingKey(生产者设定)与bindingKey一致的消息。
 

 2.2.2.2 direct模式 

direct类型交换机会将消息路由到bindingKey与routingKey完全一致的队列上。(不再特别讨论交换机绑定到交换机的情况)

  • 一个队列可以使用不同的routingKey绑定到交换机多次,用于接收多种消息。比如对于消息时日志、日志分不同级别,queue1可以配置成接收debug、warning、info、error四种消息,queue2可以配置成只接收error消息。  
2.2.2.3 topic模式

当routingKey中采用点号“.”分割时,bindingKey可以表现为类似正则表达式形式,此时队列可以接收到符合bindingKey规则的多个routingKey的消息,而不用像direct模式一样进行多次绑定。

  • 生产者发送消息时,使用的routingKey是完整的,且每个单词间用.分割。
  • bindingKey中可以使用特殊字符*和#,*用于匹配一个单词,#用于匹配零或多个单词。

如下图,对于routingKey为quick.orange.rabbitlazy.orange.elephantquick.orange.foxlazy.brown.foxlazy.pink.rabbit,Q1可以收到前3个routing Key的消息,Q2可以收到第1、2、4、5个routingKey的消息。

2.2.2.3 topic模式

当routingKey中采用点号“.”分割时,bindingKey可以表现为类似正则表达式形式,此时队列可以接收到符合bindingKey规则的多个routingKey的消息,而不用像direct模式一样进行多次绑定。

  • 生产者发送消息时,使用的routingKey是完整的,且每个单词间用.分割。
  • bindingKey中可以使用特殊字符*和#,*用于匹配一个单词,#用于匹配零或多个单词。

如下图,对于routingKey为quick.orange.rabbitlazy.orange.elephantquick.orange.foxlazy.brown.foxlazy.pink.rabbit,Q1可以收到前3个routing Key的消息,Q2可以收到第1、2、4、5个routingKey的消息。