入门RabbitMQ核心概念
消息通信概念
RabbitMQ 概述
AMQ 是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件同产品,不同的开发语言等条件的限制。高级消息队列协议使得遵从该规范的客户端应用和消息中间件服务器的全功能互操作成为可能。而 RabbitMQ 是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。
组件
遵循 AMQP 协议基本包含五个基础组件,包括 producer(生产者),publisher(消费者)、queue(队列)、message(消息实体)、exchange(交换机)。作为 AMQP 的实现者,RabbitMQ 也不例外,组件之间的协作关系图如下所述,生产者负责将消息发送到 AMQP 服务,服务通过相应的路由存储到队列中,消费者从队列中消费消息。
- producer/publisher: 消息的生产者、也叫发布者,将消息发布到 AMQP 服务;
- consumer/subscriber: 消息的消费者、订阅者,从 AMQP 服务拉取数据;
- queue: 存储消息的载体;
- message: 通讯消息实体;
- exchange: 消息的交换机 (不是必备的);
特点
RabbitMQ 在业界很受欢迎,主要得益于自身许多特点,这里列举一部分:
-
可靠性: RabbitMQ使用一些机制来保证可靠性, 如持久化、传输确认及发布确认等。
-
灵活的路由 : 在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能, RabbitMQ 己经提供了一些内置的交换器来实现。针对更复杂的路由功能,可以将多个 交换器绑定在一起, 也可以通过插件机制来实现自己的交换器。
-
扩展性: 多个RabbitMQ节点可以组成一个集群,也可以根据实际业务情况动态地扩展 集群中节点。
-
高可用性 : 队列可以在集群中的机器上设置镜像,使得在部分节点出现问题的情况下队 列仍然可用。
-
多种协议: RabbitMQ除了原生支持AMQP协议,还支持STOMP, MQTT等多种消息 中间件协议。
-
多语言客户端 :RabbitMQ 几乎支持所有常用语言,比如 Java、 Python、 Ruby、 PHP、 C#、 JavaScript 等。
-
管理界面 : RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集 群中的节点等。
-
插件机制 : RabbitMQ 提供了许多插件 , 以实现从多方面进行扩展,当然也可以编写自 己的插件。
应用
RabbitMQ 在应用场景大概有三种:解耦、削峰、异步;
- 解耦:通过消息队列中间件的引入,降低各模块之间的复杂度,达到解耦的目的。举个例子:A 系统实现发送数据给 B C D 三个子系统,那么 A 系统需实现编写调用三个字子的功能代码,此外还需考虑是否请求成功,如果失败是否尝试多次请求。引进消息队列中间件,只需要将消息推送到队列中,哪个系统需要就从队列中获取。
- 削峰:通过缓存高峰时候的请求到队列中,达到削峰的目的。举个例子:由于数据库的短板,系统的最大处理能力只能每秒处理 2000 个请求,那么可以在订阅端设置每次从队列获取 2000 条消息进行处理,多出来的请求将保留在队列中,从而不冲垮数据库。此时队列相当与一个蓄水池。高峰时把消息蓄起来,不至于冲垮下游。
- 异步:消息异步投递,通过消息队列的引入,有更加灵活的处理方式(主动拉取,服务推送),并且可以堆叠消费者进行并行处理消息。举个例子:A系统接收一个请求,需要在自己本地写库,还需要在 B C D 三个系统写库,自己本地写库要 3ms, B C D 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是3 + 300 + 450 + 200 = 953ms,接近1s,如果这些操作不需要严格串行,那么只需要将数据发送到队列,其他系统获取完各自完成写库操作,这样就达到了异步的效果。那么整个处理就差不多 3ms。
RabbitMQ 核心组件
交换机
发布者发布消息,消息首先到达交换机,交换机和网络中的交换机起到的作用一样,根据路由键转发消息到绑定的队列。RabbitMQ 中默认提供了四种交换机,为什么说默认,因为用户可以进行扩展。
交换机类型
- 直连交换机(Direct Exchange):所有发送到直连交换机的消息被转发到路由键中指定的 Queue,Direct 模式可以使用 RabbitMQ 自带的Exchange:default Exchange,所以不需要将Exchange进行任何绑定(binding)操作,消息传递时,RouteKey 必须完全匹配才会被队列接收,否则该消息会被抛弃。
- 扇形交换机(Fanout Exchange):所有达到扇形交换机上的消息将会被广播。扇形交换机会把能接收到的消息全部发送给绑定在自己身上的队列。扇形交换机不需要处理路由绑定关系,所以处理消息的速度也是所有的交换机类型里面最快的。
- 主题交换机(Topic Exchange):直连交换机的routing_key方案非常简单,如果我们希望一条消息发送给多个队列,那么这个交换机需要绑定上非常多的routing_key,假设每个交换机上都绑定一堆的routing_key连接到各个队列上。那么消息的管理就会异常地困难。所以 RabbitMQ 提供了一种主题交换机,发送到主题交换机上的消息需要携带指定规则的routing_key,主题交换机会根据这个规则将数据发送到对应的(多个)队列上。主题交换机的routing_key需要有一定的规则,交换机和队列的bindin-g_key需要采用.#......的格式,每个部分用.分开,其中:'*'表示一个单词、'#'表示任意数量(零个或多个)单词。
- 头部交换机(Header Exchange):首部交换机是忽略 routing_key 的一种路由方式。路由器和交换机路由的规则是通过 Headers 信息来交换的。将一个交换机声明成首部交换机,绑定一个队列的时候,定义一个 Hash 的数据结构,消息发送的时候,会携带一组 hash 数据结构的信息,当Hash的内容匹配上的时候,消息就会被写入队列。
交换机属性
除了交换类型之外,交换还声明了许多属性,其中最重要的是:
- Name:交换机名称;
- Type:交换机类型,direct、topic、 fanout、 headers;
- Durability:是否需要持久化;
- Auto Delete:当最后一个绑定到 Exchange 上的队列删除后,自动删除该 Exchange;
- Internal:当前 Exchange 是否用于 RabbitMQ 内部使用,默认为 False;
- Arguments:扩展参数,用于扩展 AMQP 协议定制化使用;
交换机可以是持久的,也可以是暂时的。持久化交换机在 broker 重新启动后仍能存活,而没有持久化的交换机则不会(当 broker 重新联机时,必须重新声明它们)。并非所有方案和用例都要求交换机是持久的。
队列
队列时存放消息的载体,在使用队列之前,必须声明它。声明队列将导致创建队列(如果该队列尚不存在)。如果队列已存在,并且其属性与声明中的属性相同,则该声明将不起作用。当现有的队列属性与声明中的队列属性不同时,将引发代码为 406(PRECONDITION_FAILED)的通道级异常。
队列与交换机有类型的属性,但也有一些其他属性:
Name:队列名称,不能用 amq. 开头命名;
Durability:持久化,如果为 durable,那 broker 重启不会丢失,如果为 transient,broker 重启后会丢失;
Auto delete:最后一个消费者退订时被自动删除;
Arguments:队列的其他参数,如上图,比如消息的TTL等;
这里需要注意:应用程序可以选择队列名称或要求代理为其生成名称。队列名称最多可包含 255 个字节的 UTF-8 字符。AMQP 0-9-1 代理可以代表应用程序生成唯一的队列名称。要使用此功能,请传递一个空字符串作为队列名称参数。生成的名称将返回给客户端,并带有队列声明响应。以"amq."开头的队列名称保留供代理内部使用。尝试声明名称违反此规则的队列将导致回复代码为 403 (ACCESS_REFUSED) 的通道级异常。
绑定和路由键
绑定是交换机用来(除其他事项外)将消息路由到队列的规则。要指示交换机 E 将消息路由到队列 Q,必须将 Q 绑定到 E。绑定可能具有某些交换类型使用的可选路由键属性。路由密钥的目的是选择要路由到绑定队列的发布到交易所的某些消息。换句话说,路由键的作用类似于筛选器。
数据流转
如图为官网提供的模型,蓝色框表示 Send Message,Clien t端把消息投递到Exchange上,通过 routing_key 路由关系将消息路由到指定的队列,绿色框代表 Receive Message,Client 端和队列建立监听,然后去接收消息。红色框代表 RabbitMQ Server,黄色框表示RoutingKey,即Exchange和Queue需要建立绑定关系,这是 Rabbitmq 数据流转的整体过程。
虚拟主机
RabbitMQ 是一个多组件系统:Connection、Exchange、Queue、Binding、User Permision、Policies 和其他属于虚拟主机(实体的逻辑分组)的组件。
RabbitMQ 的虚拟主机的设计思想与 Apache 的虚拟主机或 Nginx 的 Server 相似,不同的是,Apache 中的虚拟主机是定义在配置文件中,而 RabbitMQ 的虚拟主机通过 rabbitmqctl 工具或者HTTP API 进行创建和删除。
逻辑与物理分离
虚拟主机提供逻辑分组和资源分离。物理资源的分离应该将其视为虚拟主机的具体实现。
例如,RabbitMQ 中的资源权限限定在每个虚拟主机的范围内。一个用户没有全局权限,只有一个或多个虚拟主机的权限。用户标签可以被认为是全局权限,但它们是规则的例外。因此,在讨论用户权限时,阐明它们适用于什么虚拟主机是非常重要的。即用户拥有的资源权限是限定在虚拟主机范围内。
虚拟主机与客户连接
虚拟主机有其名称。当一个客户端在连接到 RabbitMQ 时,需要指定要连接的虚拟主机名称。如果认证成功并且提供的用户名被授予指定虚拟主机的权限,连接将成功建立。连接到指定虚拟主机后,只能操作该虚拟主机中的Exchange、Queue、Binding 等等。