文章目录
1. 简介
AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有很多公开标准(如 COBAR的 IIOP ,或者是 SOAP 等),但是在异步消息处理中却不是这样,只有大企业有一些商业实现(如微软的 MSMQ ,IBM 的 Websphere MQ 等),因此,在2006年6月,Cisco 、Redhat、iMatix 等联合制定了 AMQP 的公开标准。
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
RabbitMQ是由RabbitMQ Technologies Ltd开发并且提供商业支持的。该公司在2010年4月被SpringSource(VMWare的一个部门)收购。在2013年5月被并入Pivotal。其实VMWare,Pivotal和EMC本质上是一家的。不同的是VMWare是独立上市子公司,而Pivotal是整合了EMC的某些资源,现在并没有上市。
RabbitMQ官网:http://www.rabbitmq.com
下面将重点介绍RabbitMQ中的基础概念,了解这些概念,是用好RabbitMQ的前提。
2. 基本对象
ConnectionFactory、Connection、Channel都是RabbitMQ对外提供的API中最基本的对象。
ConnectionFactory为Connection的制造工厂。
Connection是RabbitMQ的socket链接,它封装了socket协议相关部分逻辑,他建立一个TCP连接,生产者和消费者的都是通过TCP连接到RabbitMQ Server中。
Channel是与RabbitMQ打交道的最重要的一个接口,大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。Channel是虚拟连接,建立在上面TCP连接的基础上,数据流动都是通过Channel来进行的。为什么不是直接建立在TCP的基础上进行数据流动呢?如果建立在TCP的基础上进行数据流动,建立和关闭TCP连接有代价。频繁的建立关闭TCP连接对于系统的性能有很大的影响,而且TCP的连接数也有限制,影响了系统的高并发的能力。
下图为RabbitMQ的架构图:
3. Queue
Queue(队列)是RabbitMQ的内部对象,用于存储消息。
RabbitMQ中的消息都只能存储在Queue中,生产者生产消息并最终投递到Queue中,消费者可以从Queue中获取消息并消费。
多个消费者可以订阅同一个Queue,这时Queue中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理,后面我们会对ACK进行介绍。
为了便于后续的理解,先先介绍以下2个概念:
- Routing Key:指定当前消息被谁接收。
- Binding Key:指定当前Exchange下,什么样的RoutingKey会被下派到当前绑定的Queue中
3.1 Queue的基本属性
- Type
此Queue的类型,默认为Classic主队列,也可以设置为Quorum从队列
- Name
必填项,队列的名字,建议格式可以为多个字段,表示队列中存放的内容,比如task.queue
- Durability
是否需要持久化,有两个选项Durable(持久化)和Transient(临时的)
- Auto delete
是否自动删除,如果选择Yes,则消息会被其中一个消费者所消费,之后队列会自动销毁,其他消费者也会断开连接(队列都没了,连接肯定断了)。
- Arguments
RabbitMQ有很多的参数可以设置,下面进行简单介绍:
x-message-ttl
:值为Number类型,表示消息在队列中的存活时间,单位为毫秒
x-expires
:值为Number类型,表示队列存在的时间,单位为毫秒
x-max-length
:值为Number类型,一个队列中能够存放的最多消息个数,超过设定个数之后就会从head开始丢弃消息。也就是先到的消息会先被丢弃。
x-max-length-bytes
:值为String类型,队列中每个消息的最大字节数,超过设定个数之后就会从head开始丢弃消息。也就是先到的消息会先被丢弃。
x-overflow
:值为String类型,设置队列溢出时的行为,可选值为drop-head或reject-publish
x-dead-letter-exchange
:值为String类型,如果消息被拒绝或者过期后,消息被重新放入的exchange名称
x-dead-letter-routing-key
:值为String类型,当消息dead-lettered,根据routing key 进行路由消息,如果没有设置,会使用消息的原始routing key
x-max-priority
:值为Number类型,最大的消息优先级,如果不设置,则不支持消息优先级
x-queue-mode
:值为String类型,如果设为lazy,则会将尽可能多的消息保存到磁盘上,减少内存的使用,如果不设置,则所有消息都放到内存,保证最快速度的分发
x-queue-master-locator
:值为String类型,当在集群中时,设置队列为master location mode,会决定队列master在集群中的位置
3.2 消息确认机制(ACK)
在实际应用中,可能会发生消费者收到Queue中的消息,但没有处理完成就宕机(或出现其他意外)的情况,这种情况下就可能会导致消息丢失。为了避免这种情况发生,我们可以要求消费者在消费完消息后发送一个回执给RabbitMQ,RabbitMQ收到消息回执后才将该消息从Queue中移除。
如果RabbitMQ没有收到回执并检测到消费者的RabbitMQ连接断开,则RabbitMQ会将该消息发送给其他消费者(如果存在多个消费者)进行处理。这里不存在Timeout的概念,一个消费者处理消息时间再长也不会导致该消息被发送给其他消费者,除非它的RabbitMQ连接断开。
这里会产生另外一个问题,如果我们的开发人员在处理完业务逻辑后,忘记发送回执给RabbitMQ,这将会导致严重的bug,Queue中堆积的消息会越来越多,消费者重启后会重复消费这些消息并重复执行业务逻辑。
3.3 Prefetch count
前面讲到如果有多个消费者同时订阅同一个Queue中的消息,Queue中的消息会被平摊给多个消费者。这时如果每个消息的处理时间不同,就有可能会导致某些消费者一直在忙,而另外一些消费者很快就处理完手头工作并一直空闲的情况。我们可以通过设置prefetchCount来限制Queue每次发送给每个消费者的消息数,比如我们设置prefetchCount=1,则Queue每次给每个消费者发送一条消息;消费者处理完这条消息后Queue会再给该消费者发送一条消息。
4. Exchange
生产者产生的消息并不是直接发送给消息队列Queue的,而是要经过Exchange,由Exchange再将消息路由到一个或多个Queue,还会对不符合路由规则的消息进行丢弃掉,这与后面要介绍的Exchange Type有关。
Exchange是怎样将消息准确的推送到对应的Queue的呢?RabbitMQ是通过Binding将Exchange和Queue链接在一起,这样Exchange就知道如何将消息准确的推送到Queue中去。在绑定(Binding)Exchange和Queue的同时,一般会指定一个Binding Key,生产者将消息发送给Exchange的时候,一般会产生一个Routing Key,当Routing Key和Binding Key对应上的时候,消息就会发送到对应的Queue中去。
4.1 Exchange的基本属性
由于前面Queue中已经介绍了Durability和Auto delete,这里就不再赘述了,下面介绍其他模块:
- exchange
交换器的名称。
- type
交换器的类型,常见的有fanout、direct、topic、headers这四种,由于Exchange Type这个属性内容较多,下面会单独进行详细的介绍。
- Internal
Internal设置是否是RabbitMQ内部使用,默认false。如果设置为true,则表示是内置的交换器,客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式。
- Arguments
扩展参数,用于扩展AMQP协议自制定化使用。
alternate-exchange
:设置“备用交换”参数,如果无法路由到exchange,将其发送到指定的备用exchange。
4.2 Exchange Type
Exchange有四种类型,不同的类型有不同的策略。不同的类型决定绑定的Queue不同,生产者发送了一个消息,Routing Key的规则是A,那么生产者会将Routing Key=A的消息推送到Exchange中,这时候Exchange中会有自己的规则,对应的规则去筛选生产者发来的消息,如果能够满足Exchange的内部规则就将消息推送到对应的Queue中去。
4种 Exchange Type 的对比:
类型名称 | 类型描述 |
---|---|
fanout | 把所有发送到该Exchange的消息路由到所有与它绑定的Queue中 |
direct | Routing Key==Binding Key |
topic | Routing Key 与 Binding Key 的模糊匹配 |
headers | 不依赖routing key与binding key的匹配规则来路由消息,根据发送的消息内容中的headers属性进行匹配。 |
下面详细讲解Exchange的类型。
- fanout
广播,fanout会把所有发送到该Exchange的消息路由到所有与它绑定的Queue中。
适用场景:
-
大型玩家在玩在线游戏的时候,可以用它来广播重大消息。
-
体育新闻实时更新到手机客户端。
-
群聊功能,广播消息给当前群聊中的所有人。
-
direct
定向,它会把消息路由到那些binding key与routing key完全匹配的Queue中。
适用场景:
-
这种类型的Exchange,通常是将同一个message以一种循环的方式分发到不同的Queue,即不同的消费者手中,使用这种方式,值得注意的是message在消费者之间做了一个均衡,而不是说message在Queues之间做了均衡。
-
topic
上面讲到direct类型的Exchange路由规则是完全匹配binding key与routing key,但这种严格的匹配方式在很多情况下不能满足实际业务需求。
topic类型的Exchange在匹配规则上进行了扩展,它与direct类型的Exchage相似,也是将消息路由到binding key与routing key相匹配的Queue中。
通配符匹配规则如下:
- Routing Key为一个句点号
.
分隔的字符串(我们将被句点号.
分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit” - Binding Key与routing key一样也是句点号
.
分隔的字符串 - Binding Key中可以存在两种特殊字符
*
与#
,用于做模糊匹配,其中*
用于匹配一个单词,#
用于匹配多个单词(可以是零个)
Topic Exchange是根据Routing Key和Exchange的类型将message发送到一个或者多个Queue中,我们经常拿他来实现各种publish/subscribe,即发布订阅,这也是我们经常使用到的ExchangeType。
适用场景:
-
新闻的分类更新
-
同意任务多个工作者协调完成
-
同一问题需要特定人员知晓
-
headers
header模式与routing不同的地方在于,header模式取消Routing Key,使用header中的k/v键值对匹配队列。
在绑定Queue与Exchange时指定一组键值对,当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。
该类型的Exchange没有用到过,暂时不做介绍,后面如果有深入使用,再详细介绍。
5. 其他相关概念补充
- Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue
- Producer:消息生产者,即生产方客户端,生产方客户端将消息发送
- Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。
生产者发送消息流程:
- 生产者和Broker建立TCP连接。
- 生产者和Broker建立通道。
- 生产者通过通道消息发送给Broker,由Exchange将消息进行转发。
- Exchange将消息转发到指定的Queue(队列)
消费者接收消息流程:
- 消费者和Broker建立TCP连接
- 消费者和Broker建立通道
- 消费者监听指定的Queue(队列)
- 当有消息到达Queue时Broker默认将消息推送给消费者。
- 消费者接收到消息。
- ack回复
参考文章
- https://blog.youkuaiyun.com/kavito/article/details/91403659
- https://blog.youkuaiyun.com/dandanzmc/article/details/52262850
- https://www.cnblogs.com/williamjie/p/9481774.html
- https://www.cnblogs.com/panchanggui/p/10330064.html
- https://blog.youkuaiyun.com/ad132126/article/details/83539213
- https://www.cnblogs.com/yaochc/p/9273321.html
- https://blog.youkuaiyun.com/a972669015/article/details/117629155