1. AMQP 0-9-1模型简介(官网直译)
关于这篇指导文档
本文提供了AMQP 0-9-1协议的一个概述,它是RabbitMQ所支持的协议之一。
AMQP 0-9-1是什么
AMQP 0-9-1(Advanced Message Queuing Protocol)高级消息队列协议是一个消息协议,它支持符合标准的客户端请求程序与符合标准的消息中间件代理进行通信。
broker与他们的角色
消息代理接收来自publishers(发布消息的应用程序,同时也可以称之为producers)的消息并且将消息路由至consumers(处理消息的应用程序)
因为它是一个网络协议,publishers,consumers与broker可以存在于不同的机器上
AMQP 0-9-1 Model概要
AMQP 0-9-1 Model的整体视图如下:
- 消息被发布到exchanges,通常可将exchanges比作邮局或者邮箱
- exchanges将消息副本分发到queues,按照bindings中的规则
- AMQP brokers传递消息给与queues关联的consumers,或者consumers按照需求从queues拉取信息
当发布一条消息时,publishers可以指定多个消息属性(message meta-data)。部分的meta-data可能会被broker使用,但是剩下消息对broker是完全不透明的,仅仅会被接收消息的应用程序使用。
网络不稳定,应用程序可能会无法处理消息,因此AMQP model有一个消息确认的概念:当一条消息被传输到consumer,consumer自动通知broker或者应用程序开发人员选择这么做。当消息确认被使用时,broker只在接收到该消息(群消息)的通知时才会从queue中完全移除该消息。
在某些场合中,比如当一条消息不能被路由时,消息可能会被返回给publishers,或者被丢弃,或者如果broker实现了一个扩展将消息放在“dead letter queue”中。publishers通过使用某些参数发布消息选择如何处理这些情况
queues,exchanges与bindings统称为AMQP实体
AMQP是一个可编程的协议
AMQP 0-9-1是一个可编程的协议,AMQP实体与路由方案都在自身应用程序中被定义,而不是被broker管理员定义。因此,为申明queues,exchanges,定义它们之间的bindings,订阅queues等协议操作进行了规定。
这给应用程序开发者很多自由空间但是也要求他们小心潜在的定义冲突。实际上定义冲突是很少见的并且通常表明是配置错误。
应用程序申明他们需要的AMQP实体,定义需要的路由方案,并且可能选择删除AMQP实体在他们不再需要使用时。
Exchanges与exchanges类型
当消息被发送时exchanges是AMQP实体。exchanges会传递信息并且将它路由到0个或者多个queue中,路由算法是取决于exchange类型与bingding规则。AMQP 0-9-1 broker提供4个exchange类型。
除exchange类型之外,exchange声明了一系列的属性,最重要的如下:
- Name
- Durability(broker重启时exchanges是否survive)
- Auto-delete(当所有queue完成使用后exchange被删除)
- Arguments(这些都是broker相关的)
Exchanges可以是持久的也可以是短暂的。持久的exchanges可以在broker重启时存活而暂时性的exchanges则不可以(当broker重新在线时必须重新申明它们),并非所有的场景与使用情况都需要持久的exchanges。
Default exchange
default exchange是一个没有名称的(空字符串)被broker预先申明的direct exchange。它所拥有的一个特殊属性使它对于简单的应用程序很有作用:每个创建的queue会与它自动绑定,使用queue名称作为routing key。
举例说,当你申明一个名称为“search-indexing-online”的queue时,AMQP broker使用“search-indexing-online”作为routing key将它绑定到default exchange。因此,一条被发布到default exchange并且routing key为"search-indexing-online"将被路由到名称为"search-indexing-online"的queue。换句话说,default exchange使直接传送消息到queue成为可能,即使从技术角度上而言,事实并不是这样。
Direct exchange
direct exchange根据消息的routing key来传送消息。direct exchange是单一传播路由消息的最佳选择(尽管他们也可以用于多路传播路由),以下是它们的工作原理:
- 一个routing key为K的queue与exchange进行绑定
- 当一条新的routing key为R的消息到达direct exchange时,exchange 将它路由至该queue如果K=R
direct exchange经常用于在多个工作者(同一应用程序的多个实例)之间分配任务。当做这些的时候,重要的是明白在AMQP 0-9-1中,消息是在consumer间负载均衡,而不是在queue。direct exchange可以用图形方式表示如下:
Fanout exchange
fanout exchange路由消息到所有的与其绑定的queue中,忽略routing key。如果N个queue被绑定到一个fanout exchange,当一条新消息被发布到exchange时,消息会被复制并且传送到这N个queue。fanout exchange是广播路由的最佳选择。
因为一个fanout exchange传送消息的副本到每一个与其绑定的queue,它的使用情况很相似:
- 大量的多用户在线(multi-player online MMO)游戏使用它更新排行榜或者其他的全体事件
- 体育新闻网站使用fanout exchange向手机客户端实时发送比分更新
- 分布式系统可以广播各种状态与配置更新
- 群聊可以使用fanout exchange让消息在参与者之间传输(即使AMQP没有内在的概念,所以XMPP也许是一个更好地选择)
一个fanout exchange图形化的表述如下
Topic exchange
Topic exchange路由消息到一个或者多个queue,基于消息的routing key和queue与exchange之间的绑定模式的匹配。Topic exchange经常被用于实现各种发布/订阅模式的变化。Topic exchanges通常被用于多路广播路由消息。
topic exchange有非常多的应用场景。当一个问题牵涉到多个consumer/应用程序,他们有选择的选择他们接收何种何种类型的消息。可以考虑使用topic exchange。
使用示例:
- 销售与特定地理位置相关的数据,比如销售点
- 由多个工作者完成的后台任务处理,每个都能够负责处理指定的任务
- 库存价格更新(更新其他的财务数据)
- 包含分类与标签的新闻更新(例如只针对某一个特定的运动或团队)
- 不同种类的云服务编制
- 分布式结构/特定操作系统软件的构建与包装,每个处理者只能处理一个结构或者系统
Headers exchange
header exchange为在多个属性进行路由而设计的,这些属性更容易描述为消息头,而不是routing key。headers exchanges忽略routing key属性,相反用于路由的属性是从headers属性中获取的。如果消息头的值等于指定的绑定值,则认为消息是匹配的。
可以使用多个header匹配将一个queue绑定到header exchange。在这种情况下,broker需要从应用程序开发者那边获取多条信息,也就是说,是否应该考虑任何headers匹配的消息,还是所有headers都匹配的消息?这就是所谓的“x-match”绑定参数。当“x-match”参数的值被设为“any”,只要一个匹配的header值就足够了。相反的,设置“x-match”的值为“all”需要所有的headers值匹配。
Headers exchanges被视为“direct exchanges on steroids”。因为他们依据headers值路由消息,他们可以被当做direct exchanges使用,routing key不必是一个字符串;举例来说它可以是一个整数或者一个hash(dictionary)
Queues
AMQP模型中的Queues相似余其他massage-和task-queueing系统中的queues:它们存储被应用程序消耗的消息。Queues与exchanges分享一些数据,但是也有一些其他的附加属性:
- Name
- Durable(当broker重启时,queue是否存在)
- Exclusive(只被一个connection使用并且在connection关闭时queue被删除)
- Auto-delete(当最后一个consumer取消订阅时queue被删除)
- Arguments(一些broker使用它去实现message TTL之类的附加功能)
queue必须在使用前被申明。如果它不存在的话申明一个queue将会创建一个queue。如果queue已经存在并且属性与申明的属性相同的情况下,申明无效。当现有的queue属性与什么的属性不相同时,一个错误码为406(先决条件失败)的channel-level异常被抛出。
Queue Names
应用程序可以选择queue名称或者请求broker重新为其生成一个名称。Queueu名称最多可达255字节。为了让AMQP broker为你生成一个唯一的queue名称,传递一个空字符串作为queue的名称参数。在相同的通道中,通过使用queue名称预期的空字符串,可以在随后的方法中获取相同的名称。这是有效的,因为channel记得最后的服务器生成的queue名称。
以“amq.”开头的queue是broker为内部使用而保留的。尝试使用违反此规则的queue名称将会导致错误码403的channel-level异常。
Queue Durability
持久的queue被持久化于磁盘中因此broker重启后仍存在。非持久的queue是暂时的。并非所有的场景与使用情况都要求queue是持久的。
queue的持久性不能让路由到queue中的消息持久化。如果broker被关闭然后重新启动,持久的queue将会在broker启动期间被重新申明,但是仅仅持久的消息会恢复。
Bindings
bindings是exchanges用来路由消息到queues的规则。为了命令exchange E路由消息到 queue Q,Q必须绑定到E。Bindings可能有一个选择性的routing key属性,被某些类型的exchanges使用。routing key是为了选择被发布到exchange的消息,路由到绑定的queue中。换句话说,routing key的功能就像一个过滤器。
画一个类比:
- queue就像是你在纽约的目的地
- Exchanges就像是JFK机场
- Bindings是JFK到你目的地的路线。有0或者多条路线到达目的地
有了间接层,就可以通过直接发布到queue来实现不可能或者很难实现的场景,并且减少应用程序开发者必须要做的某些重复工作。
如果AMQP消息不能路由到任何queue(例如,因为没有绑定到它被发布的exchange),它会被丢弃还是返回给publisher,取决于publisher设置的消息属性。
Consumers
存储消息到queue中是无用的,除非应用程序可以消耗它们。在AMQP模型中,应用程序有两种方法这么做:
- 将消息传送给它们(push API)
- 按照需要拉取消息(pull API)
使用“push API”,应用程序必须表现出对从特定queue中消耗消息的兴趣,当它们这么做时,我们说它们注册了一个消费者或者简单的订阅了一个queue。一个queue可能同时有多个consumer或者注册一个独占的消费者(当它在消耗消息时排除队列中所有其他的consumer)
Message Acknowledgements
消费者应用程序,接收处理消息的应用程序可能偶尔在处理消息时失败或者崩溃。这也可能是网络问题所导致的。这引发了一个问题:AMQP何时从queue中移除消息?AMQP 0-9-1提供了两个选择:
- broker发送消息给应用程序之后(使用basic.deliver或者basic.get-ok AMQP方法)
- 应用程序发送确认之后(使用basci.ack AMQP方法)
前一种选择被称作自动确认模型,而后者被称为显示确认模型。使用显示确认模型,应用程序可以选择何时发送确认,可以在接收消息后,或者在处理之前将其持久化到数据存储之后,或者在完全处理完成消息之后(例如成功拉取一个web页面,处理并且将其存储到持久化的数据存储中)。
如果一个consumer没有发送确认就死亡,AMQP broker将其重新发送到另外一个consumer,或者如果此时没有任何consumer,broker在尝试重新发送之前,会等待到至少有一个consumer注册到相同的queue
Rejecting Messages
当一个consumer接收到一条消息,处理消息可能成功也可能失败。一个应用程序通过拒绝消息可以向broker表明消息处理失败(或者不能在那时候完成)。当拒绝一条消息时,应用程序可以请求broker丢弃或者重新将其加入queue中。当queue中仅有一个consumer时,确保你没有重复的通过拒绝并且将消息重新加入queue中来创建无限的消息传递循环。
Negative Acknowledgements
使用AMQP的basic.reject方法拒绝消息。basci.reject方法有一个限制:就像你执行确认操作一样不可能同时拒绝多条消息。然而,如果你使用RabbitMQ,有一个解决方法。RabbitMQ提供了被称为是否定确认或者nacks的AMQP 0-9-1扩展。获取更多信息,请参照the help page.
Prefetching Messages
对于多个consumer共享一个queue的情况,在发送确认之前能指定多少条消息可以同时发送给每个消费者是非常有用的。这可以被当做一个简单的负载均衡技术使用或者提高生产量如果消息是批量发布的。例如,如果生产的应用程序每分钟发送消息是因为它的工作性质
注意RabbitMQ仅支持channel-level的预取数值,而不是链接或者预取的次数。
Messages Attributes and Payload
AMQP模型中的消息拥有属性,AMQP定义的一些属性是很常见的,应用程序开发者不需要考虑准确的属性名称,一些示例如下:
- Content type
- Content encoding
- Routing key
- Delivery mode(持久或非持久)
- Message priority
- Message publishing timestamp
- Expiration period
- Publisher application id
一些属性被AMQP broker使用,但是大多数被接收它们的应用程序所解译。有些属性是可选的被称为headers。它们与HTTP中的X-Headers相似。当消息被发布时它们的属性被设置。
AMQP消息也有有效负荷(它们所携带的数据),被AMQP brokers视为不透明字节数组。broker不会检查有效负荷。消息只包含属性没有效负荷是可能的。使用序列化格式比如JSON是很常见的。缓冲协议与数据包序列化结构数据,以便于将数据作为有效负荷发布。AMQP通常使用“content-type”与“content-encoding”字段来传达这些信息,但是仅按照规定进行。
消息可能以持久化的形式发布,这使得AMQP broker将它们持久化到磁盘中。如果服务被重启系统确保它们所接收的持久化的消息不会丢失。简单的将消息发布到一个持久的exchange或者它们被路由到的queue是持久的并不能使消息持久。消息的持久性完全取决于它本身。发布持久化的消息会影响性能(就像数据存储,持久性在性能上有一定的成本)。
Message acknowledgements
因为网络是不可靠的,应用程序也会失败,所以需要某种类型的处理确认。有时仅仅需要确认消息已被接收的事实,有时确认表示消息被确认有效并且被consumer所处理,例如被确认为是强制性的数据且被持久化到数据存储或索引中。
这种情况是非常常见的,所以AMQP 0-9-1有一个内置的被称为消息确认的功能(有时被称为acks),consumers用来确认消息传送或者处理。如果应用程序崩溃(当连接关闭时AMQP broker发出通知)。如果对于消息的确认是预期的但是没有被AMQP broker接收到,消息会被重新加入queue(可能理解被传送到另外一个consumer,如果有其他consumer存在)。
协议中的消息确认帮助开发者开发出更强壮的软件。
AMQP 0-9-1 方法
AMQP 0-9-1被构造成一系列的方法。方法即操作(像HTTP方法),而且与面向对象开发语言的方法不相同。AMQP方法被分组为类。类仅仅是AMQP方法的逻辑分组。AMQP 0-9-1 reference提供了所有关于AMQP方法的完整介绍。
让我们看一下exchange类,一系列有关exchange操作的方法。它包含了一下的操作。
- exchange.declare
- exchange.declare-ok
- exchange.delete
- exchange.delete-ok
(请注意,RabbitMQ网站参考资料也包含了对exchange类特属于RabbitMQ的扩展,这些我们不在这篇文章中讨论)
以上的操作行成了逻辑队:exchange.declare和exchange.declare-ok,exchange.delete和exchange.delete-ok。这些操作是“请求”(被客户端发送)和“响应”(brokers在响应前面提到的“请求”时发送)。
例如,客户端请求broker使用exchange.declare方法申明一个新的exchange:
如上图所示,exchange.declare携带了几个参数。它们使客户端能够指定exchange名称,类型,持久性等待。
如果操作成功,broker使用exchange.declare-ok方法响应。
除了channel号码exchange.declare-ok不携带任何参数(稍后将在这篇文章后面描述channel)
事件的顺序与另外一组AMQP queue类中的方法很相似:queue.declare和queue.declare-ok:
并不是所有的AMQP方法都有与之相对应的方法。一些(basic.publish是使用最广发的一个)没有相对于的“响应”方法和其他一些(例如basic.get)有多个可能的“响应”。
Connections
AMQP连接通常是长期存活的。AMQP是一个应用级协议,它使用TCP保持稳定传输。AMQP连接使用身份认证并且可以使用TLS(SSL)来保护连接。当应用程序不再需要连接到AMQP broker时,它应该优雅的关闭AMQP连接而不是突然关闭底层的TCP连接。
Channels
一些应用程序需要多余AMQP broker进行多连接。然而在同一时间保持多个TCP连接是不可取的,因为这么做消耗系统资源并且让配置防火墙变得更加困难。AMQP 0-9-1连接是与channel多路复用的,channel被认为是“共享单个TCP连接的轻量级连接”。
对于使用多线程或者多进程进行处理的应用程序,在每一个线程或者进程中打开一个新的channel而不是彼此间共享一个channel是非常常见的。
一个特定channel上的通信与另一个channel上的通信是完全分开的,所以每个AMQP方法也包含channel号码,客户端用该channel号码来计算方法用于哪个通道(比如哪个事件处理程序需要被激活)
Virtual Hosts
为了让单个broker能够托管多个隔离的“环境”(成群的用户,exchanges,queues等等),AMQP加入了virtual host的概念(vhosts)。它们与许多流行的web服务器使用的virtual hosts相似,并且为AMQP实体提供完全的隔离环境。AMQP客户端指定了在AMQP连接回话期间需要用的vhosts。
AMQP 是可扩展的
AMQP 0-9-1有几个扩展点:
- Custom exchange types允许开发者实现路由选择策略,而exchange类型提供的开箱即用方式不能完全的覆盖,例如geodata-based路由。
- 申明exchanges与queues可以包含broker可使用的附加属性。例如在RabbitMQ中,per-queue message TTL就是按这种方式实现的。
- 对于协议中特定于broker的扩展,例如,extensions that RabbitMQ implements.
- New AMQP 0-9-1 method classes可以被引入。
- Broker可以使用additional plugins扩展代理,例如,RabbitMQ management前端和HTTP API是以插件的形式实现的。
这些特性让AMQP 0-9-1模型更加灵活,适用于非常广泛的问题。
AMQP 0-9-1客户生态系统
许多流行的开发语言与平台都有很多RabbitMQ management。它们中的某些严格的遵循了AMQP术语并且仅仅提供AMQP方法的实现。其他的有附加的功能,方便的方法与抽象。一些客户端是异步的(非阻塞的)。一些是同步的(阻塞的),一些支持两种模型的。一些客户端支持特定于供应商的扩展(比如特定于RabbitMQ的扩展)。
因为AMQP的主要目标之一是可互操作性,对于开发者而言,理解协议操作是一个非常好的主意,而不不是受特定客户端术语的限制。用这种方式与使用不同的库的开发者沟通将会很简单。
文章来源:http://www.rabbitmq.com/tutorials/amqp-concepts.html