Table of Contents
概述
AMQP(Advanced Message Queuing Protocol),即高级消息队列协议,是一种应用层网络协
议,它为特定客户端应用(application)与消息中间件代理(messaging middleware broker)
之间的通信提供支持。AMQP 协议有过多个版本,本文所说的是 0-9-1,针对AMQP 0-9-1 协
议模型作一个简单的介绍,该模型即 rabbitmq 所使用的模型。
模型简介
消息代理(message brokers)从发布者/生产者(publishers/producers)接收消息,然后根据
既定的路由规则把接收到的消息发送给消息的处理者,即消费者(consumers)。
消息代理(message brokers)包括两部分,交换机(exchange)和队列(queue)。
发布者(publisher)发布消息(message)时可以为消息设置很多属性(message meta-data)。
消息到达交换机(exchange)之后,交换机参照这些属性将消息路由到合适的队列(queue)
中去。
从安全角色考虑,网络并不可靠,接收消息的应用也可能出错。基于此AMQP模型中包含了消
息确认(message acknowledgements)的概念,即消息从队列投递到消费者手中之后,消费者
会返回给消息代理一个确认消息,该过程可以为自动的也可以由应用来执行。该机制启用之
后,消息代理不会将消息从队列中删除,除非收到了确认消息。
交换机
AMQP 0-9-1 的消息代理提供以下四种类型的交换机:直连交换机 (Direct exchange)、扇形
交换机 (Fanout exchange)、主题交换机 (Topic exchange)、头交换机 (Headers exchang
e)。
声明交换机时,可以指定一些属性,最重要的有:
- Name (名称)
- Durability (消息代理重启后,交换机是否还存在)
- Auto-delete (当所有与此交换机绑定的队列都不再使用此交换机时是否自动删除)
- Arguments (代理自定义)
每种交换机的特点如下:
-
默认交换机
默认交换机是消息代理默认就有的没有名字的直连交换机,特点是每个新建的队列都会自
动绑定到默认交换机上,此绑定的路由关键字(routing key)与队列名称相同。 -
直连交换机
直连交换机是根据消息携带的路由关键字投递到对应队列的,用来处理消息的单播路由。 -
扇形交换机
扇形交换机将消息路由到绑定在其身上的所有队列,用来处理消息的广播路由。 -
主题交换机
主题交换机通过对消息的路由关键字和队列到交换机的绑定模式之间的匹配,将消息路由
给一个或多个队列。主题交换机通常用来实现消息的多播路由。 -
头交换机
有时消息的路由操作会涉及到多个属性,此时使用消息头就比用路由关键字更容易表达。
头交换机使用多个消息属性来代替路由键建立路由规则。通过判断消息头的值能否与指定
的绑定相匹配来确立路由规则。
头交换机可以视为直连交换机的另一种表现形式。头交换机能够像直连交换机一样工作,
不同之处在于头交换机的路由规则是建立在头属性值之上,而不是路由键。路由键必须是
一个字符串,而头属性值则没有这个约束,它们甚至可以是整数或者哈希值(字典)等。
队列
队列中存储着即将被消费掉的消息。队列与交换机共享某些属性,但队列也有一些额外的
属性:
- Name (名称)
- Durable (消息代理重启后,队列依旧存在)
- Exclusive (只被一个连接(connection)使用,而且当连接关闭后队列即被删除)
- Auto-delete (当最后一个消费者退订后即被删除)
- Arguments (一些消息代理用他来完成类似与TTL的某些额外功能)
队列在声明(declare)后才能被使用。如果一个队列尚不存在,声明一个队列会创建它。
如果声明的队列已经存在,且属性完全相同,那么此次声明不会对原有队列产生任何影响。
如果声明中的属性与已存在队列的属性有差异,那么一个错误代码为406的通道级异常就会
被抛出。
队列的名称可以由应用来指定,也可以由消息代理来生成一个,以”amq.”开头的
队列由消息代理内部使用。
持久化队列(Durable queues)会被存储在磁盘上,当消息代理(broker)重启的时候,它
依旧存在。没有被持久化的队列称作暂存队列(Transient queues)。持久化的队列并不
会使得路由到它的消息也具有持久性。
绑定
绑定为交换机将消息路由给队列所遵循的规则。由路由关键字或者消息头来定义。
当消息无法路由到队列时,AMQP会将消息销毁或者返回给发布者,具体取决于消息所携带的
属性。
消费者
消费者是消息最终的目的地,在队列中的消息会以下面两种方式投递给消费者:
- 将消息投递给应用 (“push API”)
- 应用根据需要主动获取消息 (“pull API”)
使用push API的情况,需要应用注册一个消费者,明确表示感兴趣的消息是哪一种。一个队
列可以注册多个消费者,也可以注册一个独享的消费者(当独享的消费者存在时,其它消费
者即被排除在外。)
-
消息确认
关于消息确认存在两种模式:
- 当消息代理将消息发送给应用后立即删除。
- 待应用发送一个确认回执之后再删除消息。
前者被称为自动确认模式(automatic acknowledgement model),后者被称为显式确认模式
(explicit acknowledgement model)。显式模式下,由消费者应用决定何时发送确认消息,
如果一个消费者在尚未发送确认消息的情况下挂掉了,那AMQP代理会将消息重新投递给另一
个消费者。如果当时没有可用的消费者了,消息代理会一直等下一个注册到此队列的消费者
,然后再次尝试投递。 -
拒绝消息
应用可以拒绝消息,当拒绝某条消息时,应用可以告知消息代理如何处理这条消息,是销毁
还是重新放入队列。AMQP中不能一次拒绝多个需要确认的消息,在rabbitmq中可以通过一个
扩展来解决:negative acknowledgements。 -
预取消息
AMQP允许在多个消费者共享一个队列的情况下,指定在收到下一条确认消息之前每个消费者
可以预取多少条消息。rabbitmq只支持通道级的预取计数。
消息
消息对象带有很多属性,例如:
- Content Type (内容类型)
- Content encoding (内容编码)
- Routing key (路由关键字)
- Delivery mode (持久或非持久)
- Message priority (消息优先权)
- Message publishing timestamp (消息时间戳)
- Expiration period (消息有效期)
- Publisher application id (发布者的id)
有些属性是消息代理需要用的,有些属性是消息的应用才会用到的。消息头即是某些属性的
组合。
消息能够以持久化的方式发布,AMQP代理会将此消息存储在磁盘上。如果服务器重启,系统
会确认收到的持久化消息未丢失。将消息以持久化方式发布时,会对性能造成一定的影响
(就像数据库操作一样,健壮性的存在必定造成一些性能牺牲)。
连接与通道
AMQP连接是基于TCP的长连接。当一个应用需要断开到消息代理的连接时,需要优雅的释放
连接,而不是直接关闭。
当应用需要与消息代理建立多个连接的时候,同时开多个TCP连接会带来一定程度的资源
浪费,防火墙配置也将变得复杂。AMQP提供了通道来应对多连接的情况,同一个TCP连接中
可以存在多个相互隔离的通道。通道不可被多个线程/进程共享。
虚拟主机
为了在一个单独的代理上实现多个隔离的环境(用户、用户组、交换机、队列 等),AMQP
提供了一个虚拟主机(virtual hosts – vhosts)的概念。这跟Web servers虚拟主机
概念非常相似,这为AMQP实体提供了完全隔离的环境。当连接被建立的时候,AMQP客户端
来指定使用哪个虚拟主机。
协议实现
AMQP的实现有很多,有些严格按照标准,有些则提供一些额外扩展。
我们所熟悉的实现包括:
更多实现请参考:Advanced Message Queuing Protocol