RabbitMQ(三):AMQP协议

目录

1 AMQP协议

1.1 AMQP协议介绍

因为RabbitMQ是一种遵循AMQP协议的分布式消息中间件,RabbitMQ实现的AMQP版本是0.9.1,所以在此处简单了解一下AMQP-0-9-1 协议。

1、AMQP是什么

AMQP,全称Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议。它支持符合要求的客户端应用(application)和消息中间件代理(messaging middleware broker)之间进行通信。

2、消息代理中间件的职责

Messaging Broker,这里称为消息中间件代理。它会从发布者(Publisher,或者有些时候称为Producer,生产者)接收消息,并根据既定的路由规则把消息发送给处理消息的消费者(Consumer,或者有些时候称为Listener,监听者)。

因为消息中间件代理、发布者客户端和消费者客户端都是基于AMQP这一网络消息协议,所以消息中间件代理、发布者客户端和消费者客户端可以在不同的机器上,从而实现分布式通讯和服务解耦。

消息中间件代理不仅仅提供了消息接收和消息路由这两个基本功能,还有其他高级的特性如消息持久化功能、监控功能等等。

1.2 AMQP 0-9-1模型

1、AMQP的工作过程

AMQP的工作过程如下:

  • 1.消息(message)被发布者(publisher)发送给交换器(exchange),交换器常常被比喻成邮局或者邮箱。
    • a.发布者(publisher)发布消息时可以给消息指定各种消息属性(message meta-data)
    • b.有些属性有可能会被消息代理(brokers)使用 c.其他的属性则是完全不透明的,只能被接收消息的应用使用
  • 2.交换器将收到的消息根据路由规则分发给绑定的队列(queue)
    • a.在某些情况下,例如当一个消息无法被成功路由时,消息可能会被返回给发布者,也可能被丢弃
    • b.如果消息代理执行了延期操作,消息则会被放入一个死信队列中 c.针对以上两种情况,消息发布者可以选择某些参数来处理
  • 3.AMQP代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取
    • a.由于网络的不可靠性,接收消息的应用可能在处理消息的时候失败,为了防止应用处理失败,同时消息代理中又没有该消息,一般会启用"消息确认"。当"消息确认"被启用的时候,消息代理不会完全将消息从队列中删除,直到它收到来自消费者的确认回执(acknowledgement)。

消息确认(message acknowledgements):当一个消息从队列中投递给消费者后(consumer),消费者会通知一下消息代理(broker)。可以是自动的,也可以是处理消息的应用的开发者执行。

AMQP实体(AMQP entities):队列,交换器和绑定的统称

2、交换器和交换器类型

交换器是用来发送消息的AMQP实体。交换器拿到一个消息之后将它路由给一个或零个队列。它使用哪种路由算法是由交换器类型和被称作绑定(bindings)的规则所决定的。

AMQP 0-9-1的代理提供了四种交换器:

Name(交换器类型) Default pre-declared names(预声明的默认名称)
Direct exchange(直连交换器) (Empty string) and amq.direct
Fanout exchange(扇型交换器) amq.fanout
Topic exchange(主题交换器) amq.topic
Headers exchange(头交换器) amq.match (and amq.headers in RabbitMQ)

在声明交换器时可以附带许多其他的属性,比较重要的有:

  • Name:交互器的名称
  • Type:交换器的类型
  • Durability :(交换器)持久化特性,如果启动此特性,则Broker重启后交换器依然存在,否则交换器会被删除
  • Auto-delete :是否自动删除,如果启用此特性,当最后一个队列解除与交换器的绑定关系,交换器会被删除。
  • Arguments:可选参数,一般配合插件或者Broker的特性使用

交换器状态:

  • 持久(durable):持久化的交换器会在消息代理(broker)重启后依旧存在。但是持久化的交换器不适用所有常见
  • 暂存(transient):暂存的交换器则不会(它们需要在代理再次上线后重新被声明)

几种交换器的对比:

  • 默认交换器:

    • 定义:实际上是一个由消息代理预先声明好的没有名字(名字为空字符串)的直连交换器(direct exchange)
    • 特殊属性:每个新建队列(queue)都会自动绑定到默认交换器上,绑定的路由键(routing key)名称与队列名称相同
  • 直连交换器:

    • 定义:根据消息携带的路由键(routing key)将消息投递给对应队列的
    • 处理对象:单播路由(unicast routing)
    • 工作过程:
      • 将一个队列绑定到某个交换器上,同时赋予该绑定一个路由键(routing key)
      • 当一个存在路由键为R的消息被发送给直连交换器时,交换器会把它路由给绑定值同样为R的队列。
    • 工作图:直连交换器路由图
  • 扇型交换器

    • 定义:将消息路由给绑定到它身上的所有队列,而不理会绑定的路由键
    • 处理对象:广播路由(broadcast routing)
    • 工作过程:如果某个扇型交换器上绑定了n个队列,当有消息发送给此扇型交换器时,交换器会将消息的拷贝分别发送给所有的N个队列。
    • 案例:大规模多用户在线(MMO)游戏可以使用它来处理排行榜更新等全局事件
    • 工作图:扇形交换器路由图
  • 主题交换器

    • 定义:通过对消息的路由键和队列到交换器的绑定模式之间的匹配,将消息路由给一个或多个队列
    • 处理对象:多播路由
    • 使用场景:实现各种分发/订阅模式及其变种
    • 案例:
      • 由多个工作者(workers)完成的后台任务,每个工作者负责处理某些特定的任务
      • 涉及到分类或者标签的新闻更新(例如,针对特定的运动项目或者队伍)
  • 头交换器

3、队列

队列属性

AMQP中的队列(queue):存储即将被应用消费的消息。

队列也存在其他属性:

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

队列需要被声明才能使用:

  • 如果一个队列不存在,声明队列会创建它
  • 如果声明的队列已经存在,并且属性相同,再次声明对原队列没有影响
  • 如果声明的队列已经存在,并且属性不相同,则会报错,报报错码为406
队列名称

队列的名字可以是消息代理(broker)生成,也可以是应用确定。队列命名有几个规则:

  • 队列名字是字符串,最多255字节,并且是utf-8编码
  • 以"amq."开始的队列名称被预留做消息代理内部使用
队列持久化

持久化队列(Durable queues)会被存储在磁盘上,当消息代理(broker)重启的时候,它依旧存在。没有被持久化的队列称作暂存队列(Transient queues)。并不是所有的场景和案例都需要将队列持久化。

队列的持久化特性并不意味着路由到它上面的消息是持久化的,也就是说**队列的持久化跟消息的持久化是两回事。**倘若消息代理挂掉了,重新启动,那么在重启的过程中持久化队列会被重新声明,无论怎样,只有经过持久化的消息才能被重新恢复。

1.3 几个概念

1、绑定

绑定(Binding)是交换机(exchange)将消息(message)路由给队列(queue)所需遵循的规则。

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

例如:

  • 队列:是要去的位于伦敦的目的地
  • 交换器:是国际机场ABC
  • 绑定:是国际机场ABC到伦敦的路线。能够到达目的地的路线可以是一条或者多条
    如果AMQP的消息无法路由到队列(例如,发送到的交换机没有绑定队列),消息会被就地销毁或者返还给发布者。如何处理取决于发布者设置的消息属性。

2、消费者

消费者用来接收发送者发送的消息,消费者一般是应用程序。消费者消费消息的这个操作的实现方式一般有两种:

  • 消息代理中间件向消费者推送消息(推模式push API,代表方法是basic.consume)。
    • 前提:消费者必须指定需要订阅的队列。每个队列可以存在多个消费者,或者仅仅注册一个独占的消费者。
  • 消费者主动向消息代理中间件拉取消息(拉模式 pull API,代表方法是basic.get)。

每个消费者(订阅者)都有一个叫做消费者标签的标识符。它可以被用来退订消息。消费者标签实际上是一个字符串。

3、消息确认

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

  • 当消息代理(broker)将消息发送给应用后立即删除。(使用AMQP方法:basic.deliver或basic.get-ok) → 这种模式又称自动确认模式(automatic acknowledgement model)
  • 待应用(application)发送一个确认回执(acknowledgement)后再删除消息。(使用AMQP方法:basic.ack) → 这种模式又称显式确认模式(explicit acknowledgement model)

在显式模式下,由消费者应用来选择什么时候发送确认回执(acknowledgement),规定了AMQP消息代理未收到消费者回执后的操作:

  • 1.应用可以在收到消息后立即发送,可以将未处理的消息存储后发送,也可以等到消息被处理完毕后再发送确认回执。
  • 2.如果一个消费者在尚未发送确认回执的情况下挂掉了,那AMQP代理会将消息重新投递给另一个消费者。如果当时没有可用的消费者了,消息代理会死等下一个注册到此队列的消费者,然后再次尝试投递。

4、预取消息

预取消息(Prefetching Messages)是一个特性。如果多个消费者共享同一个队列,能够告知消息中间件代理在发送下一个确认之前指定每个消费者一次可以接收消息的消息量。

这个特性可以理解为简单的负载均衡技术,在批量发布消息的场景下能够提高吞吐量。

注意,RabbitMQ只支持通道级的预取计数,而不是连接级的或者基于大小的预取。

5、消息属性和有效载荷(消息主体)

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(发布应用的ID)

AMQP的消息除属性外,也含有一个有效载荷 - Payload(消息实际携带的数据),它被AMQP代理当作不透明的字节数组来对待。

消息代理不会检查或者修改有效载荷。消息可以只包含属性而不携带有效载荷。

常使用序列化格式(如JSON,Thrift,Protocol Buffers和MessagePack)来序列化和结构化数据,以便将其作为消息有效负载发布。在一般约定下,消息属性中的Content type和 Content encoding一般可以表明其序列化的方式。

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

6、连接

AMQP连接通常是长连接。AMQP是一个使用TCP提供可靠投递的应用层协议。AMQP使用认证机制并且提供TLS(SSL)保护。当一个应用不再需要连接到AMQP代理的时候,需要优雅的释放掉AMQP连接,而不是直接将TCP连接关闭。

7、通道

有些应用需要与AMQP代理建立多个连接。但是,同时开启多个TCP连接都是不合适的,因为这样做不仅会消耗掉过多的系统资源,同时会使得防火墙的配置更加困难。AMQP 0-9-1提供了通道(channels)来处理多连接,可以把通道理解成共享一个TCP连接的多个轻量化连接。

在涉及多线程/进程的应用中,为每个线程/进程开启一个通道(channel)是很常见的,并且这些通道不能被线程/进程共享,每个特定的通道和其他通道是相互隔离的,每个执行的AMQP操作方法(包括响应)都携带一个通道的唯一标识,这样客户端就能通过该通道的唯一标识得知操作方法是对应哪个通道发生的。

8、虚拟主机

为了在一个单独的代理上实现多个隔离的环境(用户、用户组、交换机、队列 等),AMQP提供了一个虚拟主机(virtual hosts - vhosts)的概念。这为AMQP实体提供了完全隔离的环境。当连接被建立的时候,AMQP客户端可以在连接消息中间件代理时指定需要连接的虚拟主机。

相关推荐
jerry6094 小时前
7天用Go从零实现分布式缓存GeeCache(改进)(未完待续)
分布式·缓存·golang
古人诚不我欺5 小时前
jmeter常用配置元件介绍总结之分布式压测
分布式·jmeter
星染xr7 小时前
kafka 生产经验——数据积压(消费者如何提高吞吐量)
分布式·kafka
东方巴黎~Sunsiny7 小时前
如何监控Kafka消费者的性能指标?
分布式·kafka
飞升不如收破烂~7 小时前
kafka
分布式·kafka
简 洁 冬冬8 小时前
037 RabbitMQ集群
rabbitmq·集群
龙哥·三年风水9 小时前
群控系统服务端开发模式-应用开发-前端个人信息功能
分布式·vue·群控系统
小码哥呀10 小时前
RabbitMQ集群搭建
分布式·rabbitmq
材料苦逼不会梦到计算机白富美10 小时前
golang分布式缓存项目 Day6 防止缓存击穿
分布式·缓存·golang
Yz987612 小时前
Kafka面试题
大数据·分布式·zookeeper·kafka·big data