2 RabbitMQ 概念介绍

felix.shao2025-02-18

2 RabbitMQ 概念介绍

TIP

 本小节主要介绍以下知识:

  • RabbitMQ 模型架构,含概念和流程介绍
  • AMQP 协议介绍

RabbitMQ 结构

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

生产者和消费者

Producer:投递消息的一方。
 生产者创建消息,然后发布到 RabbitMQ 中。消息一般包含2个部分:消息体(payload)和标签(Label)。消息体如 json 字符串;标签用来表述这条消息,比如一个交换器的名称和一个路由键。 RabbitMQ 会根据标签把消息发送给感兴趣的消费者。

Consumer:接收消息的一方。
 消费者连接到 RabbitMQ 服务器,并订阅到队列上。当消费者消费一套消息时,只是消费消息的消息体(payload)。在消息路由的过程中,消息的标签会丢弃,存入到队列中的消息只有消息体,消费者也只会消费到消息体,也就不知道消息的生产者是谁,当然消费者也不需要知道。

Broker:消息中间件的服务节点。
 一个 RabbitMQ Broker 可以简单地看作一个 RabbitMQ 服务节点,或者 RabbitMQ 服务实例。大多数情况下也可以将一个 RabbitMQ Broker 看作一台 RabbitMQ 服务器。

 下图展示了生产者将消息存入 RabbitMQ Broker,以及消费者从 Broker 中消费数据的整个流程。
message_queue_opt_process.jpg

  • 发送到 Broker,对应 AMQP 协议里的命令为 Basic.Publish
  • 订阅并接收消息,对应 AMQP 协议里的命令为 Basic.Consume 或者 Basic.Get
  • 接收消息和业务处理逻辑并不一定是同一个线程

队列

 RabbitMQ 的内部队列,用于存储消息。

  • RabbitMQ 中消息都只能存储在队列中,和 Kafka 相反。 Kafka 将消息存储在 topic 逻辑层面,而相应的队列逻辑只是 topic 实际存储文件中的位移标识。RabbitMQ 的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。
  • 多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(Round-Robin,即轮询)给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。
  • RabbitMQ 不支持队列层面的广播消费,如果需要广播消费,需要在其上进行二次开发,处理逻辑会变得异常复杂,同时也不建议这么做。

交换器、路由键、绑定

Exchange:交换器。
 生产者将消息发送到 Exchange,由交换器将消息路由到一个或者多个队列中。如果路由不到(没有配置一个路由),或者会返回给生产者,或许直接丢弃。
RabbitMQ 中的交换器有四种类型,不同的类型有着不同的路由策略,路由策略下一小节中介绍。

RoutingKey:路由键。
 生产者将消息发给交换器的时候,一般会指定一个 RoutingKey,用来指定这个消息的路由规则,而这个 Routing Key需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。

Binding:绑定。
 RabbitMQ 中通过绑定将交换器与队列关联起来,在绑定的时候一般会指定一个绑定键(BindingKey),这样 RabbitMQ 就知道如何正确地将消息路由到队列了。
bindkey_hint.jpg

  • RoutingKey 与 BindingKey 在某些情形下(如direct交换器下,RoutingKey和BindingKey需要完全匹配才能使用),可以看作同一个东西。
  • RoutingKey 与 BindingKey 在 topic 交换器泪下下,Routing 和 BindingKey 之间需要做模糊匹配,两者并不是相同的。

TIP

 为了避免混淆,可以这么理解:

  • 在使用绑定的时候,其中需要的路由键是 BindingKey。涉及的客户端方法如:channel.exchangeBind 、channel.queueBind ,对应的AMQP命令为 Exchange.Bind、Queue.Bind。
  • 在发送消息的时候,其中需要的路由键是 RoutingKey。涉及的客户端方法如:channel.basicPublish,对应的AMQP命令为 Basic.Publish。

交换器类型

 RabbitMQ 常用的交换器类型有 fanout、direct、topic、headers 这四种。AMQP 协议理还提到另外两种类型:System 和自定义,这里不予描述。

fanout:
 它会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中。

direct:
 它会把消息路由到那些 BindingKey 和 RoutingKey 完全匹配的队列中,对比fanout多了个路由匹配规则。

topic:
 它与 direct 类型的交换器相似,也是将消息路由到 BindingKey 和 RoutingKey 相匹配的队列中,但这里的匹配规则有些不同,它约定:

  • RoutingKey 为一个点号"."分隔的字符串(被点号"."分割开的每一段独立的字符串称为一个单词),如"com.rabbitmq.client"。
  • BindingKey 和 RoutingKey 一样也是点号"."分隔的字符串。
  • BindingKey 中可以存在两种特殊字符串""和"#",用于做模糊匹配,其中""用于匹配一个单词,"#"用于匹配多个单词(可以是零个)。

 示例:

交换器->路由->队列配置如下:
Exchange->Route(*.rabbitmq.*)->Queue1
Exchange->Route(*.*.client)->Queue2
Exchange->Route(com.#)->Queue2

实际路由时:
1. 路由键"com.rabbitmq.client"的消息会同时路由到Queue1和Queue2。
2. 路由键"com.hidden.client"的消息只会路由到Queue2中。
3. 路由键"com.hidden.demo"的消息只会路由到Queue2中。
4. 路由键"java.rabbitmq.demo"的消息只会路由到Queue1中。
5. 路由键"java.util.concurrent"的消息将会被丢弃或者返回给生产者(需要设置mandatory参数),因为他没有匹配任何路由键。

headers:
 headers 类型的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配。在绑定队列和交换器时指定一组键值对,当发送消息到交换器时,RabbitMQ 会获取到该消息的headers,对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对,如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers 类型的交换器性能会很差,而且也不适用,基本上不会看到它的存在。

RabbitMQ运转流程

 即生产者发送消息时候的运转流程和消费者接收消息的过程,具体可详见代码:

 代码中有两个新的概念,Connetion 和 Channel。其中 Connection 就是一条 TCP 连接。信道是建立在 Connection 之上的虚拟连接,RabbitMQ 处理的每条 AMQP 指令都是通过信道完成的。

TIP

 为什么引入信道?
 RabbitMQ 采用类似 NIO 的做法,选择 TCP 连接复用,不仅可以减少性能开销,同时也便于管理。复用一个 Connection 会有性能瓶颈,此时可以考虑根据业务情况开辟多个 Connection 分摊这些信道。

AMQP 协议介绍

 RabbitMQ 是遵从 AMQP 协议的,即 RabbitMQ 就是 AMQP 协议的 Erlang 的实现。
 RabbitMQ 中的交换器、交换器类型、队列、绑定、路由键等都是遵循的 AMQP 协议中相对应的概念。
 AMQP 协议本身包括三层:

  • Module Layer:位于协议最高层,主要定义了一些客户端调用的命令,客户端可以利用这些命令实现自己的业务逻辑。例如,客户端可以使用 Queue.Declare 命令声明一个队列或者使用 Basic.Consume 订阅消费一个队列中的消息。
  • Session Layer:位于中间层,主要负责将客户端的命令发送给服务器,再将服务器的应答返回给客户端,主要为客户端和服务器之间的通信提供可靠性同步机制和错误处理。
  • Transport Layer:位于最底层,主要传输二进制数据流,提供帧的处理、信道复用、错误检测和数据表示等。

 AMQP 是一个通信协议,从 low-level 层面举例来说,AMQP 本身是应用层的协议,其填充于 TCP 协议层的数据部分。从 high-level 层面来说,AMQP 是通过协议命令进行交互的。AMQP 协议可以看作一系列结构化命令的集合,这里的命令代表一种操作,类似于 HTTP 中的方法(GET、POST、PUT、DELETE等)。

 AMQP 生产者流转流程和 AMQP 消费者流转流程参考:AMQP协议、生产者流转过程、消费者流转过程open in new window

 AMQP 命令概览参考:AMQP命令概览open in new window

参考文献

  • [RabbitMQ实战指南]
Last Updated 2/18/2025, 5:05:12 PM