原文链接

  • 什么是AMQP 0-9-1 ?

    AMQP(高级消息队列协议)是一个消息协议,使应用可以和消息中间件(消息代理)通讯。

  • AMQP中的角色

    消息代理(消息中间件,message broker)接受从发布者(发布消息的应用程序,也称为生产者)发送的消息,并路由消息到消费者(处理消息的应用程序)。 因为AMQP是网络协议,生产者、消费者和消息代理可以部署在不同的机器上。

  • AMQP 0-9-1 模型简介

    AMQP 0-9-1 模型:消息被发布到交换机(exchange),交换机然后运用路由规则(称之为绑定(binding))将消息分发复制到队列, 然后消息代理要么投递消息到订阅队列的消费者,要么消费者从队列拉取消息。

    xxx

    当发布一个消息,生产者可以指定消息属性(消息元数据),其中某些元数据可以被中间件使用。然而,剩下的消息体{//TO CHECK}部分对中间件是不可见的,只能被接受到该消息的应用程序使用。

    网络的不可靠,以及应用程序也可能处理消息失败,因此AMQP模型存在一个概念:消息确认(message acknowledgements)。当消息已经投递给消费者,该消费者发送消息确认通知代理,代理收到消息确认后从队列中将该消息删除。

    在某些情况下,比如消息不能被路由时,消息可能返回给生产者,可能丢弃,也可能放入“死信队列”,生产者通过使用一些参数来选择如何处理这些情况。

    队列,交换机和绑定都称为AMQP实体。

  • AMQP是一个可编程协议

    从某种意义上来说,AMQP 0-9-1是可编程协议。AMQP实体和路由是通过应用程序自己定义,而不是代理管理员。相应的,协议提供了一些列操作,比如申明队列和交换机,并定义他们之间的绑定,订阅队列等。

    应用程序声明他们需要的AMQP实体,定义路由规则,也可以删除它们不在使用的实体。

  • 交换机和类型

    消息送达交换机实体之后,交换机路由消息给0个或者多个队列,而路由算法依赖交换机类型和规则(或者叫绑定),AMQP 0-9-1提供4中类型的交换机类型:

    1. 直接交换机(direct)
    2. 散出交换机(fanout)
    3. 主题交换机(topic)
    4. 头交换机(header)

      除了交换机类型,声明交换机时,可以携带多个属性,最重要的是:

      • 交换机名字
      • 持久性(durability,交换机是否在代理重启后幸免)
      • 自动删除(当最后一个队列和交换机接触绑定时是否删除)
      • 其他参数(可选的,用于插件和代理特殊的特性)

      交换机可以持久化(duirable)的或者短暂(transient)的,持久化交换在代理重启后依然存在,而短暂的交换机不会(他们必须在代理恢复上线后重新被声明),不是所有的场景都需要使用持久化交换机。

  • 默认交换机

    代理会预定义一个默认的交换机,它是一个直接(direct)交换机,并且名字为空字符串。它有个非常特殊的特性:每个新创建的队列自动绑定到该交换机,路由键(routing-key)同队列的名字一样。

    比如, 当你声明一个名为“search-indexing-online”的队列,代理会使用“search-indexing-online”作为路由键绑定这个队列到默认交换机。因此,一个消息如果发布到默认交换机,并且路由键同队列同名(search-indexing-online),该消息将被路由到“search-indexing-online”队列,看起来就像是直接投递消息到队列中,虽然在技术上并非如此。

  • 直接交换机(direct exchange)

    直接交换机路由消息到队列依赖于消息路由键。直接交换机是单播路由消息的理想方式(虽然也可以使用直接交换机多播)。它是怎么运作的:

    一个队列绑定交换机,使用路由键K  
    当一个使用路由键R的新消息到达交换机后,如果K=R,交换机路由该消息到这个绑定队列。
    

    用图形来表示直接交换机:

  • 散出交换机(fanout exchange)

    一个散出交换机路由消息给绑定到它的所有队列上,并且忽略路由键。如果有N个队列绑定到散出交换机,当一个新消息发布到该散出交换机,它会复制消息并投递给N个队列中。散出交换机是广播路由的理想方式。

    用图形来表示散出交换机:

    exchange-fanout.png

  • 主题交换机(topic exchange)

    主题交换机路由消息到一个或者多个队列,通过匹配消息路由键和队列绑定到交换机的模式。主题交换机类型常用于实现多样化的发布/订阅模式,是组播消息路由的理想方式。

  • 头交换机(header exchange)

    头交换机使用比简单的路由键更有表达力的消息头中多个属性来路由消息。头交换机忽略路由键,而使用消息头中的属性来处理路由。当使用多个属性作为匹配条件时,代理需要知道是匹配所有呢,还是匹配任意一个,这就是绑定参数“x-match"的作用。

  • 队列(queue)

    队列存储消息,以供消费者使用。他有如下属性:

    1. 名称
    2. 持久性
    3. 排他性(exclusive,只能被一个连接使用,一但连接关闭,队列就被删除)
    4. 自动删除(最后一个消费者取消订阅时,队列被删除)
    5. 其他参数(可选的,比如消息TTL,队列长度限制等等)

    当一个队列不存在时,声明队列会创建一个新的队列,如果队列存在时,以相同的属性再次声明队列时,什么都不会发生,而当属性不同时,声明操作会导致一个信道(channel)级别的错误,code是406(PRECONDITION_FAILED).

  • 队列名字

    应用程序可以选择队列名字,代理也可以自动生成唯一的队列名字,通过传入一个空字符串作为名字,生成的名字会返回给声明的客户端。以“amq.”开头的队列名字是代理保留的,尝试声明“amq."开头的队列将会得到一个信道级别的错误,code403(ACCESS_REFUSED)

  • 队列持久化

    队列持久化是将队列持久化到磁盘,因此当代理重启后,队列还在。没有持久化的队列叫瞬时队列,不是所有的场景都需要将队列持久化。持久化队列并不意味着路由到该队列的消息也是持久化的,当代理挂了重启后,持久化队列会被重新声明;然而,只有持久化消息才能恢复。

  • 绑定(binding)

    绑定是交换机路由消息到队列的路由规则。想要交换机E路由消息给队列Q,Q必须绑定到E上。在某些交换机类型上,绑定可能会使用一个路由键(routing-key)的属性。

  • 消费者(consumers)

    存储在队列中的消息有两种方式可以让消费者使用:
    推(push)和拉(pull)

    在推模式中,应用程序指名要从特定的队列中消费消息,该过程称之为消费者订阅队列;可能有多个消费者同时订阅同一队列,也可能订阅一个排他的消费者(exclusive consumer,当该消费者消费消息时,排斥其他消费者)。每个消费者有个消费者标签(comsumer tag),用来做比如取消订阅操作时表明是哪个消费者,消费者标签只是一个字符串。

  • 消息确认(message acknowledgements)

    消费者可能处理单个消息失败,或者消费者就是挂了,或者网络问题,将会带来一个问题:代理在什么时候从队列中删除消息?AMQP 0-9-1有两个选择:
    在代理发送消息给消费者之后。(使用basic.deliver 或者 basic.get-ok)
    在消费者发送确认给代理之后。(使用basic.ack)

    前者称为自动确认模式,后者称为显式确认模式(explicit acknowledgement),使用显式确认模式时,消费者可以选择在何时发送消息确认给代理。如果一个消费者没有发送消息确认给代理就挂了,代理会重新投递消息给其他消费者。

  • 拒绝消息(rejecting message)

    当一个消费收到消息后,处理可能成功,也可能失败。消费者可以通过拒绝消息向代理说明消息处理失败了。拒绝消息时,消费者可以要求代理丢弃该消息,或者重入队列。需要注意的是:拒绝消息时如果要重入队列,可能会导致不停的拒绝,然后又不停的收到该消息,形成死循环。

  • 否决确认(negative acknowledgements)

    拒绝消息时,使用basic.reject方法,该方法无法拒绝多个消息。如果使用rabbitmq,它提供了一个插件称为”negative acknowledgements or nacks“可以解决。

  • 预取消息(prefetching message)

    当多个消费者订阅同一个队列时,指定每个消费在发送下一次消息确认之前可以同时接受多少消息是非常有用的,可以增加消息的吞吐量。注:rabbit-mq只支持信道级别的预取数量。

  • 消息属性和有效负荷(payload)

    消息拥有属性,如:content-type、content-encoding、routing-key、dilivery mode、message priority、publish temestamp、expiration period、publisher applicaiton id。

    一些属性供代理使用,但大多数为接受消息的应用程序服务。一些属性是可选的,称之为headers,类似HTTP中的X-headers。

    消息拥有有效负荷(消息携带的数据),代理视为不可见的字节数组,代理不会查看和修改有效负荷。消息也有可能只包含属性,不包含有效负荷。

    消息可以作为持久化消息发布,代理会持久化消息到磁盘,保障代理重启后消息不丢。简单的发布一个消息到持久化交换机不会让消息持久化,持久化依赖消息本身的持久化模式,当然,持久化消息会影响性能。

  • AMQP 0-9-1 方法(method)

    AMQP 0-9-1定义了一组操作方法,这些方法通过类别来组织(同OOP中的方法不是一个概念),详细的方法说明参考

    来看看exchange组的方法,涉及exchange相关的操作,包括下面这些操作:

    exchange.declare  
    exchange.declare-ok  
    exchange.delete  
    exchange.delete-ok  
    

    上面的操作可以从逻辑上看做是操作对:declare declare-ok 一对,delete delete-ok 一对。

例如,客户端通过exchange.declare方法要求代理声明一个新的交换机。

如果操作成功,代理通过exchange.declare-ok方法返回:

其他方法同上述非常类似。

  • 连接(connections)
    AMQP连接是长连接的,AMQP是一个应用级协议,使用TCP来可靠的投递消息。AMQP连接可以使用TLS(SSL)来验证。当一个应用程序不再需要连接到代理时,应该优雅的关闭而不是在TCP层粗暴的关闭。

  • 信道(channels)
    有些应用需要多个连接,非常不建议在同一时间保持很多TCP连接,因为这样会消耗系统资源,并且和很难配置防火墙。AMQP 0-9-1 TCP连接可以被多个信道复用,可以理解信道为“轻量级的虚拟连接,共享同一个TCP连接”
    使用多线程处理的应用,通常为每个线程打开一个信道,并且不要在多个线程中共享信道。
    不同信道上的通讯完全是隔离的,因此,每一个AMQP方法都会携带信道号(channel number)来区别该方法使用的是哪个信道。

  • 虚拟主机(vhost)
    虚拟主机让单个代理实例拥有多个独立“环境”(用户、交换机、队列等), 客户端连接到代理时,需指明连接的是哪个虚拟主机。

  • AMQP是可扩展的
    AMQP 0-9-1 有几个扩展点:
    自定义的交换机类型可以让开发者实现消息路由。定义交换机和队列时可以添加属性,比如TTL消息。代理可以通过插件来扩展,比如rabbitmq management。
    这些特性可以让AMQP 0-9-1模型更加具有扩展性。

  • AMQP 0-9-1 客户端生态
    有很多主流的语言和平台的客户端,一些遵循AMQP技术只实现AMQP的方法,一些有额外的特性,便利使用。一些客户端是异步的,一些是同步的;等等。
    建议开发者理解协议操作,而不限于具体的客户端库,这样开发者使用不同的客户端库是会容易得多。

results matching ""

    No results matching ""