Understanding of RabbitMQ AMQP-0-9-1 model

premise

Prior to have a plan before learning RabbitMQ, the AMQP read once, pick out the key elements inside. Then find an official document under the RabbitMQ, I have found a document devoted to the part of RabbitMQ AMQP model implemented, so direct and personal understanding based on this document to write this article.

AMQP protocol

AMQP stands for Advanced Message Queuing Protocol, it is a (distributed) messaging protocol, and in line with the use of this protocol client capable of delivering middleware agent (Broker based on usage and message in line with this agreement, that is, brokers, personal feeling Some hekou called proxy) to communicate. AMQP 1.0 protocol has launched to achieve this agreement more well-known products are StormMQ, RabbitMQ, Apache Qpid and so on. RabbitMQ AMQP version 0.9.1 is implemented, the official documentation of the agreement also provides text pdf download, are interested can read it.

Message middleware agent duties

Messaging Broker, referred to here as messaging middleware proxy. Its role is to receive from the publisher (Publisher, or sometimes referred to as Producer, Producer) message, and then routes the message to consumers (Consumer, or sometimes called Listener, listener).

Because the messaging middleware agent, publisher client and consumer clients of this network are based on AMQP messaging protocol, messaging middleware agent, publisher client and consumer clients can be on different machines, in order to achieve distribution decoupled communication style and service.

Messaging middleware agent not only provides message reception and message routing these two basic functions, as well as other advanced features such as message persistence capabilities, monitoring and so on.

AMQP-0-9-1 basic model of RabbitMQ

The basic view AMQP-0-9-1 model is: Post Owner messages posted to the switch (Exchange), the role of the exchange's somewhat similar to the post office or mailbox every day to see. Then, switch to a copy of the message distributed to the queue (Queue), the rules to follow when distributing messages called bindings (Binding). Subsequently, the proxy message middleware to send messages (push mode) to the subscriber queue consumer, or the consumer may also actively pull message (fetch / pull mode) from the queue.

The publisher can specify when the release messages of message attributes (message metadata), some of the message metadata may be used by the proxy messaging middleware, message metadata for other messaging middleware proxy is not transparent, only for news consumers use.

Since the network is unreliable, the client may not be able to receive messages or process messages failed, this time messaging middleware agent can not perceive the message is properly delivered to the consumer, and thus provides a conceptual model AMQP message acknowledgment (Message Acknowledgement): When message to the consumer, the consumer may be automatically acknowledgment message to the message broker agent has been successfully received by the application developer or the manual selection and acknowledgment message has been successfully received acknowledgment message to the message broker proxy, the proxy message only when it receives the middleware the message (or group of messages) will confirm delete this notification message from the queue.

In some cases, the switch can not be properly routed to the queue, then the message will be returned to the publisher, or discarded, or if the message middleware agent to achieve a "dead letter queue (Dead Letter Queue)" extended message It is placed in the dead letter queue. Post Owner may choose to use the process control strategy parameter corresponding to the route failed.

Type switch and switch

Interactor (Exchange) is the destination of the message sent by the first station, its role is to receive the message and route it to zero or more queues. Message routing algorithm depends on the type of device interaction and routing rules (ie Binding). RabbitMQ messaging middleware agent supports four types of interactive devices, namely:

Switch type Broker default predeclared switch
Direct (The empty string [(AMQP default)]) and amq.direct
Fanout amq.fanout
Topic amq.topic
Headers amq.match (RabbitMQ and in amq.headers)

Statement interaction is when the need to provide a series of attributes, one of the more important attributes are as follows:

  • Name: The name of the interactive device.
  • Type: type of the exchange.
  • Durability :( exchanger) persistence characteristic, if you start this attribute, the Broker restart the switch still exists, otherwise the exchange will be deleted.
  • Auto-delete: whether to automatically delete, if this feature is enabled, when the last queue binding relationship with the lifting of the switch, the switch will be deleted.
  • Arguments: optional parameter, with the general characteristics of the use of plug-ins or Broker.

Durability exists and Auto-delete feature is concurrent because all scenarios and use cases require interaction is persistent.

Direct Switch

Direct type switch based message routing keys (RoutingKey) the message to the queue. Direct switching is realized over a unicast routing message (of course, also be used for multicast routing), it works as follows:

  • Routing queues using the key K is bound to the exchanger.
  • When a new message arrives with routing keys exchanger R, if K = R, then the switch will transfer the message to the queue.

The default switch

The default switch (Default Exchange) is a special Direct interaction is, its name is an empty string (ie ""), which proxy statement by the pre-messaging middleware, in RabbitMQ Broker, which in the Web management interface the name is (AMQP default). Each queue will be bound to the newly created default switches, routing key is the name of the queue for the queue, that is, all queues can be delivered by the default exchange messages, you need only specify the routing keys to the appropriate queue name to .

Fanout Switch

Fanout其实是一个组合单词,fan也就是扇形,out就是向外发散的意思,Fanout交换器可以想象为"扇形"交换器。Fanout交换器会忽略路由键,它会路由消息到所有绑定到它的队列。也就是说,如果有N个队列绑定到一个Fanout交换器,当一个新的消息发布到该Fanout交换器,那么这条新消息的一个副本会分发到这N个队列中。Fanout交换器是消息广播路由的理想实现。

Topic交换器

Topic交换器基于路由键和绑定队列和交换器的模式进行匹配从而把消息路由到一个或者多个队列。绑定队列和交换器的Topic模式(这个模式串其实就是声明绑定时候的路由键,和消息发布的路由键并非同一个)一般使用点号(dot,也就是'.')分隔,例如source.target.key,绑定模式支持通配符:

  • 符号'#'匹配一个或者多个词,例如:source.target.#可以匹配source.target.dogesource.target.doge.throwable等等。
  • 符号'*'只能匹配一个词,例如:source.target.*可以匹配source.target.dogesource.target.throwable等等。

对每一条消息,Topic交换器会遍历所有的绑定关系,检查消息指定的路由键是否匹配绑定关系中的路由键,如果匹配,则将消息推送到相应队列。

Topic交换器是消息多播路由的理想实现。

Headers交换器

Headers交换器是一种不常用的交换器,它使用多个属性进行路由,这些属性一般称为消息头,它不使用路由键进行消息路由。消息头(Message Headers)是消息属性(消息元数据)部分,因此,使用Headers交换器在建立队列和交换器的绑定关系的时候需要指定一组键值对,发送消息到Headers交换器时候,需要在消息属性中携带一组键值对作为消息头。消息头属性支持匹配规则x-match如下:

  • x-match = all:表示所有的键值对都匹配才能接受到消息。
  • x-match = any:表示只要存在键值对匹配就能接受到消息。

Headers交换器也是忽略路由键的,只依赖于消息属性中的消息头进行消息路由。

队列

AMQP 0-9-1模型中的队列与其他消息或者任务队列系统中的队列非常相似:它们存储应用程序所使用的消息。队列和交换器的基本属性有类似的地方:

  • Name:队列名称。
  • Durable:是否持久化,开启持久化意味着消息中间件代理重启后队列依然存在,否则队列会被删除。
  • Exclusive:是否独占的,开启队列独占特性意味着队列只能被一个连接使用并且连接关闭之后队列会被删除。
  • Auto-delete:是否自动删除,开启自动删除特性意味着队列至少有一个消费者并且最后一个消费者解除订阅状态(一般是消费者对应的通道关闭)后队列会自动删除。
  • Arguments:队列参数,一般和消息中间件代理或者插件的特性相关,如消息的过期时间(Message TTL)和队列长度等。

一个队列只有被声明(Declare)了才能使用,也就是队列的第一次声明就是队列的创建操作(因为第一次声明的时候队列并不存在)。如果使用相同的参数再次声明已经存在的队列,那么此次声明会不生效(当然也不会出现异常)。但是如果使用不相同的参数再次声明已经存在的队列,那么会抛出通道级别的异常,异常代码是406(PRECONDITION_FAILED)。

队列名称

队列名必须由255字节(bytes)长度以内的UTF-8编码字符组成。实现AMQP 0-9-1规范的消息中间件代理具备自动生成随机队列名的功能,也就是在声明队列的时候,队列名指定为空字符串,那么消息中间件代理会自动生成一个队列名,并且在队列声明的返回结果中带上对应的队列名。

以"amq."开头的队列是由消息中间件代理内部生成的,有其特殊的作用,因此不能声明此类名称的新队列,否则会导致通道级别的异常,异常代码为403(ACCESS_REFUSED)。

队列的持久化特性

持久化的队列会持久化到磁盘中,这种队列在消息中间件代理重启后不会被删除。不开启持久化特性的队列称为瞬时(transient)队列,并非所有的场景都需要开启队列的持久化特性。

队列的持久化特性并不意味着路由到它上面的消息是持久化的,也就是队列的持久化跟消息的持久化是两回事。如果息中间件代理挂了,它重启后会重新声明开启了持久化特性的队列,这些队列中只有使用了消息持久化特性的消息会被恢复。

绑定

绑定(Binding)是交换器路由消息到队列的规则。例如交换器E可以路由消息到队列Q,那么Q必须通过一定的规则绑定到E。绑定中使用的某些交换器的类型决定了它可以使用可选的路由键(RoutingKey)。路由键的作用类似于过滤器,可以筛选某些发布到交换器的消息路由到目标队列。

如果发布的消息没有路由到任意一个目标队列,例如,消息已经发布到交换器,交换器中没有任何绑定,这个时候消息会被丢弃或者返回给发布者,取决于消息发布者发布消息时候使用的参数。

消费者

如果队列只有发布者生产消息,那么是没有意义的,必须有消费者对消息进行使用,或者叫这个操作为消息消费,消息消费的方式有两种:

  • 消息代理中间件向消费者推送消息(推模式,代表方法是basic.consume)。
  • 消费者主动向消息代理中间件拉取消息(拉模式,代表方法是basic.get)。

使用推模式的情况下,消费者必须指定需要订阅的队列。每个队列可以存在多个消费者,或者仅仅注册一个独占的消费者。

每个消费者(订阅者)都有一个称为消费者标签(consumer tag)的标识符,消费者标签是一个字符串。通过消费者标签可以实现取消订阅的操作。

消息确认

消费者应用程序有可能在接收和处理消息的时候崩溃,也有可能因为网络原因导致消息中间件代理投递消息到消费者的时候失败了,这样就会催生一个问题:AMQP消息中间件代理应该在什么时候从队列中删除消息?因此,AMQP 0-9-1规范提供了两种选择:

  • 消息中间件代理向应用程序发送消息(使用AMQP方法basic.deliverbasic.get-ok)。
  • 应用程序收到消息后向消息中间件代理发送确认(使用AMQP方法basic.ack <= 个人感觉这个地方少写了basic.nackbasic.reject)

前一种称为自动确认模型(动作触发的同时进行了消息确认),后一种称为显式确认模型。显式确认模型中,需要消费者主动向消息中间件代理进行消息主动确认,这个消息主动确认动作的执行时机完全由应用程序控制。消息主动确认有三种方式:积极确认(ack)、消极确认(nack)和拒绝(reject)。

预取消息

预取消息(Prefetching Messages)是一个特性。对于多个消费者共享同一个队列的情况,能够告知消息中间件代理在发送下一个确认之前指定每个消费者一次可以接收消息的消息量。这个特性可以理解为简单的负载均衡技术,在批量发布消息的场景下能够提高吞吐量。

消息属性和有效负载

AMQP模型中,消息具有属性值。AMQP 0-9-1规范定义了一些常见的属性,一般开发人员不需要太关注这些属性:

  • Content type
  • Content encoding
  • Routing key
  • Delivery mode (persistent or not)
  • Message priority
  • Message publishing timestamp
  • Expiration period
  • Publisher application id

这些通用的属性一般是消息中间件代理使用的,还有可以定制的可选属性header,形式是键值对,类似于HTTP中的请求头。消息属性是在发布消息的时候设置的。

AMQP消息还有一个有效载荷(payload,其实就是消息数据体),AMQP代理将其视为不透明的字节数组,也就是AMQP代理不会检查或者修改消息的有效载荷。有些消息可能只包含属性而没有有效负载。通常使用序列化格式(如JSON,Thrift,Protocol Buffers和MessagePack)来序列化和结构化数据,以便将其作为消息有效负载发布。在一般约定下,消息属性中的Content typeContent encoding一般可以表明其序列化的方式。

消息发布支持消息的持久化特性,消息持久化特性开启后,消息中间件代理会把消息保存到磁盘中,如果重启代理消息也不会丢失。开启消息持久化特性将会影响性能,主要是因为涉及到刷盘操作。

AMQP-0-9-1方法

AMQP 0-9-1定义了一些方法,对应了客户端和消息中间件代理之间交互的一些操作方法,这些操作方法的设计跟面向对象编程语言中的方法没有任何共同之处。常用的交换器相关的操作方法有:

  • exchange.declare
  • exchange.declare-OK
  • exchange.delete
  • exchange.delete-OK

在逻辑上,上面几个操作方法在客户端和消息中间件代理之间的交互如下:

对于队列,也有类似的操作方法:

  • queue.declare
  • queue.declare-OK
  • queue.delete
  • queue.delete-OK

并非所有的AMQP操作方法都有响应结果操作方法,像消息发布方法basic.publish的使用是最广泛的,此操作方法没有对应的响应结果操作方法。有些操作方法可能有多个响应结果(操作方法),例如basic.get

连接(Connection)

AMQP的连接(Connection)通常是长期存在的。AMQP是一种使用TCP进行可靠传递的应用程序级协议。AMQP连接使用用户身份验证,可以使用TLS(SSL)进行保护。当应用程序不再需要连接到AMQP代理时,它应该正常关闭AMQP连接,而不是突然关闭底层TCP连接。

通道(Channel)

某些应用程序需要与AMQP代理程序建立多个连接。但是,不希望同时打开许多TCP连接,因为这样做会消耗系统资源并使配置防火墙变得十分困难。通道(Channel)可以认为是"共享一个单独的TCP连接的轻量级连接",一个AMQP连接可以拥有多个通道。

对于使用了多线程处理的应用程序,有一种使用场景十分普遍:每个线程开启一个新的通道使用,这些通道是线程间隔离的。

另外,每个特定的通道和其他通道是相互隔离的,每个执行的AMQP操作方法(包括响应)都携带一个通道的唯一标识,这样客户端就能通过该通道的唯一标识得知操作方法是对应哪个通道发生的。

虚拟主机(Virtual Host)

为了使单个消息中间件代理可以托管多个完全隔离的"环境"(这里的隔离指的是用户组、交互器、队列等),AMQP提供了虚拟主机(Virtual Host)的概念。多个虚拟主机类似于许多主流的Web服务器的虚拟主机,提供了AMQP组件完全隔离的环境。AMQP客户端可以在连接消息中间件代理时指定需要连接的虚拟主机。

个人理解

关于Exchange、Queue和Binding

理解RabbitMQ中的AMQP模型,其实从开发者的角度来看,最重要的是Exchange、Queue、Binding三者的关系,这里谈谈个人的见解。消息的发布第一站总是Exchange,从模型上看,消息发布无法直接发送到队列中。Exchange本身不存储消息,它在接收到消息之后,会基于路由规则也就是Binding,把消息路由到目标Queue中。从实际操作来看,声明路由规则总是在发布消息和消费消息之前,也就是一般步骤如下:

  • 1、声明Exchange。
  • 2、声明Queue。
  • 3、基于Exchange和Queue声明Binding,这个过程有可能自定义一个RoutingKey。
  • 4、通过Exchange消息发布,这个过程有可能使用到上一步定义的RoutingKey。
  • 5、通过Queue消费消息。

我们最关注的两个阶段,消息发布和消息消费中,消息发布实际上只跟Exchange有关,而消息消费实际上只跟Queue有关。Binding实际上就是Exchange和Queue的契约关系,会直接影响消息发布阶段的消息路由。那么,路由失败一般是什么情况导致的?路由失败,其实就是消息已经发布到Exchange,而Exchange中从既有的Binding中无法找到存在的目标Queue用于传递消息副本(一般而言,很少人会发送消息到一个不存在的Exchange)。消息路由失败,从理解AMQP的模型来看,可以从根本上避免的,除非是消息发布者故意胡乱使用或者人为错误使用了未存在的RoutingKey、Exchange或者说是Binding关系而导致的。

关于Exchange的类型

AMQP-0-9-1模型中支持了四种交换器direct(单播)、fanout(广播)、topic(多播)、headers,实际上,从使用者角度来看,四种交换器的功能是可以相互取代的。例如可以使用fanout类型交换器实现广播,其实使用direct类型交换器也是可以实现广播的,只是对应的direct类型交换器需要通过多个路由键绑定到多个目标队列中。在面对生产环境的技术选型的时候,我们需要考虑性能、维护难度、合理性等角度去考虑选择什么类型的交换器,就上面的广播消息的例子,显然使用fanout类型交换器可以避免声明多个绑定关系,这样在性能、合理性上是更优的选择。

关于负载均衡

在AMQP-0-9-1模型中,负载均衡的实现是基于消费者而不是基于队列(准确来说应该是消息传递到队列的方式)。实际上,出现消息生产速度大大超过消费者的消费速度的时候,队列中有可能会出现消息积压。AMQP-0-9-1模型中没有提供基于队列负载均衡的特性,也就是出现消息生产速度大大超过消费者的消费速度时候,并不会把消息路由到多个队列中,而是通过预取消息(Prefetching Messages)的特性,确定消息者的消费能力,从而调整消息中间件代理推送消息到对应消费者的数量,这样就能够实现消费速度快的消费者能够消费更多的消息,减少产生有消费者处于饥饿状态和有消费者长期处于忙碌状态的问题。

关于消息确认机制

AMQP中提供的消息确认机制主要包括积极确认(一般叫ack,Acknowledgement)、消极确认(一般叫nack,Negative Acknowledgement)和拒绝(reject)。消息确认机制是保证消息不丢失的重要措施,当消费者接收到消息中间件代理推送的消息时候,需要主动通知消息中间件代理消息已经确认投递成功,然后消息中间件代理才会从队列中删除对应的消息。没有主动确认的消息就会变为"nack"状态,可以想象为暂存在队列的"nack区"中,这些消息不会投递到消费者,直到消费者重启后,"nack区"中的消息会重新变为"ready"状态,可以重新投递给消费者。关于消息确认机制其实场景比较复杂,后面再做一篇文章专门分析。

小结

参考资料:

个人博客

(本文完 e-a-20181125 c-7-d)

Guess you like

Origin www.cnblogs.com/throwable/p/12275645.html