ActiveMQ系列—消息协议(AMQP协议)

AMQP协议的全称是:Advanced Message Queuing Protocol(高级消息队列协议)。目前AMQP协议的版本为 Version 1.0,这个协议标准在2014年经过了国际标准组织 (ISO) 和国际电工委员会 (IEC) 的投票,成为了新的 ISO 和 IEC 国际化标准。目前支持AMQP的软件厂商包括:web

这里写图片描述

一、协议概览

在网络上讲解AQMP协议的文章已经有不少了,您能够在百度、Google、必应上搜索关键字‘AMQP’,就会出现不少相关文章。虽然文章数量比较多,可是却鲜有质量过硬的文章(固然除了AMQP官网 http://www.amqp.org/ 的协议说明文档)。本小节的内容我试图在能力所及的范围内,为各位读者将AMQP协议的核心要点讲清楚。为了达到这个目的,首先将AMQP协议的原理用下图进行一个全面呈现,而后在详细讲解图中的每个要点:服务器

这里写图片描述

从上图咱们能够看到AMQP协议的各个组成部分:网络

  • AMQP协议中的元素包括:Message(消息体)、Producer(消息生产者)、Consumer(消息消费者)、Virtual Host(虚拟节点)、Exchange(交换机)、Queue(队列)等;并发

  • 由Producer(消息生产者)和Consumer(消息消费者)构成了AMQP的客户端,他们是发送消息和接收消息的主体。AMQP服务端称为Broker,一个Broker中必定包含完整的Virtual Host(虚拟主机)、 Exchange(交换机)、Queue(队列)定义;app

  • 一个Broker能够建立多个Virtual Host(虚拟主机),咱们将讨论的Exchange和Queue都是虚拟机中的工做元素(还有User元素)。注意,若是AMQP是由多个Broker构成的集群提供服务,那么一个Virtual Host也能够由多个Broker共同构成;ide

  • Connection是由Producer(消息生产者)和Consumer(消息消费者)建立的链接,链接到Broker物理节点上。可是有了Connection后客户端还不能和服务器通讯,在Connection之上客户端会建立Channel,链接到Virtual Host或者Queue上,这样客户端才能向Exchange发送消息或者从Queue接受消息。一个Connection上容许存在多个Channel,只有Channel中可以发送/接受消息。svg

  • Exchange元素是AMQP协议中的交换机,Exchange能够绑定多个Queue也能够同时绑定其余Exchange。消息经过Exchange时,会按照Exchange中设置的Routing(路由)规则,将消息发送到符合的Queue或者Exchange中。ui

那么AMQP消息在这个结构中是如何经过Producer发出,又通过Broker最后到达Consumer的呢?请看下图:编码

这里写图片描述

  1. 在Producer(消息生产者)客户端创建了Channel后,就创建了到Broker上Virtual Host的链接。接下来Producer就能够向这个Virtual Host中的Exchange发送消息了;加密

  2. Exchange(交换机)可以处理消息的前提是:它至少已经和某个Queue或者另外的Exchange造成了绑定关系,并设置好了到这些Queue和Excahnge的Routing(路由规则)。Excahnge中的Routing有三种模式,咱们随后会讲到。在Exchange收到消息后,会根据设置的Routing(路由规则),将消息发送到符合要求的Queue或者Exchange中(路由规则还会和Message中的Routing Key属性配合使用);

  3. Queue收到消息后,可能会进行以下的处理:若是当前没有Consumer的Channel链接到这个Queue,那么Queue将会把这条消息进行存储直到有Channel被建立(AMQP协议的不一样实现产品中,存储方式又不尽相同);若是已经有Channel链接到这个Queue,那么消息将会按顺序被发送给这个Channel。

  4. Consumer收到消息后,就能够进行消息的处理了。可是整个消息传递的过程尚未完成:视设置状况,Consumer在完成某一条消息的处理后,将须要手动的发送一条ACK消息给对应的Queue(固然您能够设置为自动发送,或者无需发送)。Queue在收到这条ACK信息后,才会认为这条消息处理成功,并将这条消息从Queue中移除;若是在对应的Channel断开后,Queue都没有这条消息的ACK信息,这条消息将会从新被发送给另外的Channel。固然,您还能够发送NACK信息,这样这条消息将会当即归队,并发送给另外的Channel。

二、Message(消息体)

经过上一小节的描述,咱们能够看到AMQP协议中消息的处理规则和Stomp协议中消息的处理规则有相似之处,好比对ACK、NACK的使用。但明显不一样的地方仍是不少,例如AMQP中Exchange元素提供的Routing路由规则,这显然比Stomp协议中直接发送给Queue要灵活得多。

为了支持AMQP协议中的这些规则,AMQP协议中的消息也必须有特定的格式,实际上AMQP协议要比Stomp协议复杂得多。下面咱们就根据ISO/IEC发布的AMQP Version 1.0标准文档,来讨论一下AMQP协议中的消息格式。

首先要说明的是目前国内多个技术站点,详细介绍AMQP消息格式的文章原本就很少(不包括那些聊聊几笔的转发),并且基本上都没有详细讲解格式自己,只是粗略说明了AMQP消息采用二进制格式(任何应用层协议在网络上进行传输,都是使用二进制流进行的,因此这个说法固然没错)。

有的文章还向读者传递了错误的信息。例如说AMQP消息格式包括两部分:消息头和消息正文。 这是彻底错误的,虽然AMQP消息格式确实包括Header和Body部分,可是绝对不止这两个部分。(若是真是这样,ISO/IEC组织就不须要使用125页的文档篇幅来进行说明了)

首先咱们须要说明的是,做为一种网络通信协议,AMQP工做在七层/五层网络模型的应用层,是一个典型的应用层协议;另外,因为AMQP协议存在多种元素定义,且这些元素定义工做在不一样的领域。例如Channel的定义是为了基于网络链接记录会话状态;Queue等元素帮助AMQP完成路由规则,这些元素在Message消息记录中都须要有所体现。

因此AMQP协议首先要记录网络状态和会话状态,格式以下(AMQP帧的定义在《OASIS Advanced Message Queueing Protocol (AMQP) Version 1.0》文档的第38页):

这里写图片描述

其中非PAYLOAD部分,在网络协议的应用层说明Channel的工做状态(固然还有说明整个AMQP消息的长度区域:SIZE),咱们真正须要的内容存在PAYLOAD区域。PAYLOAD区域(译文称为‘交付区’)的格式以下(能够在《OASIS Advanced Message Queueing Protocol (AMQP) Version 1.0》文档的第3部分:messaging第82页找到详细说明):

这里写图片描述

在PAYLAOD区域一共包含7个数据区域:header、delivery-annotations、message-annotations、properties、application-properties、application-data、footer。这些元素的做用以下:

  • header:header部分记录了AMQP消息的在‘支持AMQP的中间件’中的交互状态。例如该条消息在节点间被交互的总次数、优先级、TTL(Time To Live)值等信息;

  • delivery-annotations:在header部分只能传递规范的、标准的、通过ISO/IEC组织定义的属性。那么若是须要在header部分传递一些非标准信息怎么办呢?这就是delivery-annotations数据区域存在的意义:用来记录那些’非标’的header信息;

  • message-annotations:这个数据区域,用于存储一些自定义的辅助属性。和delivery-annotations区域的非标准信息不一样,这里的自定义属性主要用于消息的转换。例如AMQP-JMS信息转换过程当中将依据这个数据区域的“x-opt-jms type”、“x-opt-to-type”、“x-opt-reply-type”和“name”属性进行JMS规范中对应的“JMSType”、“Type of the JMSDestination”、“Type of the
    JMSReplyTo”和“JMS_AMQP_MA_name”属相的转换;

  • properties:从整个AMQP消息的properties属性开始,到AMQP消息的application-data部分结束,才是AMQP消息的正文内容(译文称为‘裸消息’)。Properties属性记录了AMQP消息正文的那些‘不可变’属性。在properties部分只能传递规范的、标准的、通过ISO/IEC组织定义的属性。例如:消息id、分组id、发送者id、内容编码等。如下是AMQP协议文档中对Properties部分属性的描述(只能包含这些信息):

<type name="properties" class="composite" source="list" provides="section">
    <descriptor name="amqp:properties:list" code="0x00000000:0x00000073"/>
    <field name="message-id" type="*" requires="message-id"/>
    <field name="user-id" type="binary"/>
    <field name="to" type="*" requires="address"/>
    <field name="subject" type="string"/>
    <field name="reply-to" type="*" requires="address"/>
    <field name="correlation-id" type="*" requires="message-id"/>
    <field name="content-type" type="symbol"/>
    <field name="content-encoding" type="symbol"/>
    <field name="absolute-expiry-time" type="timestamp"/>
    <field name="creation-time" type="timestamp"/>
    <field name="group-id" type="string"/>
    <field name="group-sequence" type="sequence-no"/>
    <field name="reply-to-group-id" type="string"/>
</type>
  • application-properties:‘应用数据’属性,在这部分数据中主要记录和应用有关的数据,AMQP的实现产品(例如RabbitMQ)须要用这部分数据决定其处理逻辑。例如:送入哪个Exchange、消息的Routing值是什么、是否进行持久化等;

  • application-data:使用二进制格式描述的AMQP消息的用户部份内容。既是咱们发送出去的真实内容

  • footer:通常在这个数据区域存储辅助内容,例如消息的哈希值,HMAC,签名或者加密细节。

以上才是一个AMQP消息的完整结构。固然因为篇幅限制,在某一个数据区域的‘标准’属性就没有再细讲了,例如Properties数据区域定义的creation-time属性、Header数据区域定义的durable属性。

三、Exchange(交换机)路由规则

Exchange交换机在AMQP协议中主要负责按照必定的规则,将收到的消息转发到已经和它事先绑定好的Queue或者另外的Exchange中。Excahnge交换机的这个处理过程称之为Routing(路由)。目前流行的AMQP路由实现一共有三种:分别是Direct Exchange、Fanout Exchange和Topic Exchange。须要特别注意的是:Exhange须要具有怎样的‘路由’规则,并无在AMQP标准协议进行强行规定,目前流行的AMQP转发规则都是AMQP实现产品自行开发的(这也是为何AMQP消息中和路由、过滤规则相关的属性是存放在application-properties区域的缘由)。

A、Direct路由

direct模式从字面上的理解应该是‘引导’、‘直接’的含义。direct模式下Exchange将使用AMQP消息中所携带的Routing-Key和Queue中的Routing Key进行比较。若是二者彻底匹配,就会将这条消息发送到这个Queue中。以下图所示:

这里写图片描述

B、Fanout路由

Fanout路由模式不须要Routing Key。当设置为Fanout模式的Exchange收到AMQP消息后,将会将这个AMQP消息复制多份,分别发送到和本身绑定的各个Queue中。以下图所示:

这里写图片描述

C、Topic路由

Topic模式是Routing Key的匹配模式。Exchange将支持使用‘#’和‘ * ’通配符进行Routing Key的匹配查找(‘#’表示0个或若干个关键词,‘ * ’表示一个关键词,注意是关键词不是字母)。以下图所示:

这里写图片描述

为了方便各位读者的理解,这里咱们再举几个通配符匹配的示例:

  • “param.#”,能够匹配“param”、“param.test”、“param.value”、“param.test.child”等等AMQP消息的Routing Key;可是不能匹配诸如“param1.test”、“param2.test”、“param3.test”。觉得param这个关键词和param1这个关键词不相同。

  • “param.*.* ”,能够匹配“param.test.test”、“param.test.value”、“param.test.child”等等AMQP消息的Routing Key;可是不能匹配诸如“param”、“param.test”、“parm.child”等等Routing Key。

  • “param.*.value”,能够匹配“param.value.value”、“param.test.value”等Routing Key;可是不能匹配诸如“param.value”、“param.value.child”等Routing Key。

注意,以上介绍的Direct 路由模式和Topic 路由模式中,若是Exchange交换机没有找到任何匹配Routing Key的Queue,那么这条AMQP消息会被丢弃。(只有Queue有保存消息的功能,可是Exchange并不负责保存消息)