一、RabbitMQ——消息路由机制

一、Rabbit路由协议——AMQP协议

AMQP(Advanced Message Queuing Protocol,高级消息队列协议)是一个网络协议。它支持符合要求的客户端应用(application)和消息中间件代理(messaging middleware broker)之间进行通信。主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
简单点说就是在异常通讯中,消息不会立即到达接收方,而是被存放到一个容器中,当满足一定的条件之后,消息会被容器发送给接收方,这个容器就是消息队列(MQ),而完成这个功能需要双方和容器以及其中的各个组件遵循规则,AMQP就是这样一种协议,消息发送和接收的双方都要遵守。
AMQP机制为:消息(message)被发布者(publisher)发送给交换机(exchange),交换机将收到的消息根据路由规则发给绑定的队列(queue)。最后AMQP代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取,如图:
一、RabbitMQ——消息路由机制_第1张图片
这是一个简单的“Hello,world”示例,从发布者到生成者消息的大致流向,其中还省略了一些AMQP实际的组件细节。这里主要包括以下一些组件:

Publisher,数据的发送方。
Exchange,消息交换机,它指定消息按什么规则,路由到哪个队列,这里的规则后面会有介绍。
Queue,消息队列载体,每个消息都会被投入到一个或多个队列。
Consumer,数据的接收方。
发布者(Publisher)发布消息时可以给消息指定各种消息属性(message meta-data)。有些属性有可能会被消息代理(Brokers)使用,然而其他的属性则是完全不透明的,它们只能被接收消息的应用所使用。

从安全角度考虑,网络是不可靠的,接收消息的应用也有可能在处理消息的时候失败。基于此原因,AMQP模块包含了一个消息确认(message acknowledgements)的概念:当一个消息从队列中投递给消费者后(Consumer),消费者会通知一下消息代理(Broker),这个可以是自动的,也可以由处理消息的应用的开发者执行。当“消息确认”被启用的时候,消息代理不会完全将消息从队列中删除,直到它收到来自消费者的确认回执(acknowledgement)。

在某些情况下,例如当一个消息无法被成功路由时,消息或许会被返回给发布者并被丢弃。或者,如果消息代理执行了延期操作,消息会被放入一个所谓的死信队列中。此时,消息发布者可以选择某些参数来处理这些特殊情况。

队列,交换机和绑定统称为AMQP实体(AMQP entities)。

以上这些是在上图中显示出来的一些AMQP组件元件,除了这些外,还有一些额外的概念,主要为:
Binding,绑定,消息队列与交换器直接关联的,它的作用就是把Exchange和Queue按照路由规则绑定起来。

Routing Key,路由关键字,Exchange根据这个关键字进行消息投递。

Channel,信道,多路复用连接中的一条独立的双向数据流通道,为会话提供物理传输介质。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。在客户端的每个连接里,可建立多个Channel,每个Channel代表一个会话任务。

Broker ,AMQP的服务端称为Broker。其实Broker就是接收和分发消息的应用,也就是说RabbitMQ Server就是Message Broker。

Virtual Host,虚拟主机,一批交换器(Exchange),消息队列(Queue)和相关对象。虚拟主机是共享相同身份认证和加密环境的独立服务器域。同时一个Broker里可以开设多个vhost,用作不同用户的权限分离。

Connection ,连接,一个网络连接,比如TCP/IP套接字连接。Channel是建立在Connection之上的,一个Connection可以建立多个Channel。

Message,消息体,是AMQP所操纵的基本单位,它由Producer产生,经过Broker被Consumer所消费。它的基本结构有两部分: Header和Body。Header是由Producer添加上的各种属性的集合,这些属性有控制Message是否可被缓存,接收的Queue是哪个,优先级是多少等。Body是真正需要传送的数据,它是对Broker不可见的二进制数据流,在传输过程中不应该受到影响。

上面的简单实例图只是简单说明了一些消息传送的基本流程,可能其中的频道、虚拟主机、连接、信道等都没有体现出来。到后面RabbitMQ的知识点都会慢慢介绍到。

总的AMQP的结构图如下所示:
一、RabbitMQ——消息路由机制_第2张图片

下面对AMQP的一些重要概念做详细分析:

交换机(消息交换机)

交换机是用来发送消息的AMQP实体。交换机拿到一个消息之后将它路由给一个或零个队列。它使用哪种路由算法是由交换机类型和被称作绑定(bindings)的规则所决定的。

四种交换机:

Direct exchange(直连交换机)
Fanout exchange(扇型交换机)
Topic exchange(主题交换机)
Headers exchange(头交换机)
交换机可以有两个状态:持久(durable)、暂存(transient)。持久化的交换机会在消息代理(Broker)重启后依旧存在,而暂存的交换机则不会(它们需要在代理再次上线后重新被声明)。然而并不是所有的应用场景都需要持久化的交换机。

不同的交换机的区别和用法,会在后面的文章中做介绍,这里只需要知道有几种类型的交换机就可以了。

交换机的几个重要属性:
Name
Durability (消息代理重启后,交换机是否还存在)
Auto-delete (当所有与之绑定的消息队列都完成了对此交换机的使用后,删掉它)
Arguments(依赖代理本身)

队列

AMQP中的队列(Queue)跟其他消息队列或任务队列中的队列是很相似的:它们存储着即将被应用消费掉的消息。队列跟交换机共享某些属性,但是队列也有一些另外的属性。

Name
Durable(消息代理重启后,队列依旧存在)
Exclusive(只被一个连接(connection)使用,而且当连接关闭后队列即被删除)
Auto-delete(当最后一个消费者退订后即被删除)
Arguments(一些消息代理用他来完成类似与TTL的某些额外功能)

队列在声明(declare)后才能被使用。如果一个队列尚不存在,声明一个队列会创建它。如果声明的队列已经存在,并且属性完全相同,那么此次声明不会对原有队列产生任何影响。如果声明中的属性与已存在队列的属性有差异,那么一个错误代码为406的通道级异常就会被抛出。

队列持久化
持久化队列(Durable queues)会被存储在磁盘上,当消息代理(broker)重启的时候,它依旧存在。没有被持久化的队列称作暂存队列(Transient queues)。
持久化的队列并不会使得路由到它的消息也具有持久性。倘若消息代理挂掉了,重新启动,那么在重启的过程中持久化队列会被重新声明,无论怎样,只有经过持久化的消息才能被重新恢复。(队列持久化 ≠ 消息持久化)

绑定
绑定(Binding)是交换机(Exchange)将消息(Message)路由给队列(Queue)所需遵循的规则。如果要指示交换机“E”将消息路由给队列“Q”,那么“Q”就需要与“E”进行绑定。绑定操作需要定义一个可选的路由键(Routing Key)属性给某些类型的交换机。路由键的意义在于从发送给交换机的众多消息中选择出某些消息,将其路由给绑定的队列。

打个比方:

队列(Queue)是我们想要去的目的地
交换机(Exchange)是一个转换站,比如车站
绑定(Binding)就是车站到目的地的路线。能够到达目的地的路线可以是一条或者多条
拥有了交换机这个中间层,很多由发布者直接到队列难以实现的路由方案能够得以实现,并且避免了应用开发者的许多重复劳动。

如果AMQP的消息无法路由到队列(例如,发送到的交换机没有绑定队列),消息会被就地销毁或者返还给发布者。如何处理取决于发布者设置的消息属性。

虚拟主机

为了在一个单独的代理上实现多个隔离的环境(用户、用户组、交换机、队列 等),AMQP提供了一个虚拟主机(virtual hosts - vhosts)的概念。这跟Web servers虚拟主机概念非常相似,这为AMQP实体提供了完全隔离的环境。当连接被建立的时候,AMQP客户端来指定使用哪个虚拟主机。

连接

AMQP连接通常是长连接。AMQP是一个使用TCP提供可靠投递的应用层协议。AMQP使用认证机制并且提供TLS(SSL)保护。当一个应用不再需要连接到AMQP代理的时候,需要优雅的释放掉AMQP连接,而不是直接将TCP连接关闭。

信道

有些应用需要与AMQP代理建立多个连接。无论怎样,同时开启多个TCP连接都是不合适的,因为这样做会消耗掉过多的系统资源并且使得防火墙的配置更加困难。AMQP 0-9-1提供了信道(channels)来处理多连接,可以把信道理解成共享一个TCP连接的多个轻量化连接。

在涉及多线程/进程的应用中,为每个线程/进程开启一个信道(channel)是很常见的,并且这些通道不能被线程/进程共享。

二、RabbitMQ的工作流程介绍

  1. 建立信息。Publisher定义需要发送消息的结构和内容。
  2. 建立Conection和Channel。由Publisher和Consumer创建连接,连接到Broker的物理节点上,同时建立Channel。Channel是建立在Connection之上的,一个Connection可以建立多个Channel。Publisher连接Virtual Host 建立Channel,Consumer连接到相应的Queue上建立Channel。
  3. 声明交换机和队列。声明一个消息交换机(Exchange)和队列(Queue),并设置相关属性。
  4. 发送消息。由Publisher发送消息到Broker中的Exchange中
  5. 路由转发。RabbitMQ收到消息后,根据​​消息指定的Exchange(交换机) 来查找Binding(绑定) 然后根据规则(Routing Key)分发到不同的Queue。这里就是说使用Routing Key在消息交换机(Exchange)和消息队列(Queue)中建立好绑定关系,然后将消息发送到绑定的队列中去。
  6. 消息接收。Consumer监听相应的Queue,一旦Queue中有可以消费的消息,Queue就将消息发送给Consumer端。
  7. 消息确认。当Consumer完成某一条消息的处理之后,需要发送一条ACK消息给对应的Queue。

    • Consumer收到消息时需要显式的向RabbitMQ Broker发送basic.ack消息或者 Consumer订阅消息时设置auto_ack参数为true。
    • 在通信过程中,队列对ACK的处理有以下几种情况:
    • 如果Consumer接收了消息,发送ack,RabbitMQ会删除队列中这个消息,发送另一条消息给Consumer。
    • 如果Consumer接收了消息, 但在发送ack之前断开Channel,RabbitMQ会认为这条消息没有被deliver(递送),如果有其他的Channel,会该消息将被发送给另外的Channel。如果没有,当在Consumer再次连接的时候,这条消息会被redeliver(重新递送)。
    • 如果consumer接收了消息,但是忘记了ack,RabbitMQ不会重复发送消息。
    • 新版RabbitMQ还支持Consumer reject某条(类)消息,可以通过设置requeue参数中的reject为true达到目地,那么Consumer将会把消息发送给下一个注册的Consumer。
  8. 关闭消息通道(channel)以及和服务器的连接。

你可能感兴趣的:(rabbitmq)