「消息队列」消息队列概述与AMQP协议

版权声明:本文为博主原创文章,转载必须注明出处。 https://blog.csdn.net/jinixin/article/details/83552185

转载请注明出处:https://blog.csdn.net/jinixin/article/details/83552185

前面几篇文章中谈了rpc服务, rpc可用于进程间通信, 使应用得以解耦, 而进程间通信还可使用消息队列来完成.

本篇文章就简单谈谈消息队列, 以及其所遵守的AMQP协议.

消息队列的定义

消息队列, 从字面上看其必和队列有一定关系. 而队列想必大家也都有所了解, 其是一种基本的数据结构, 先入先出, 可以起到暂存数据, 共享数据的作用. 消息队列和前面的队列类似, 只是其额外还支持数据持久化, 处理确认, 流量控制, 跨服务器投递等功能.

官方定义消息队列(Message Queue)是一种进程间通讯或同一进程不同线程间通讯的方式. 消息队列提供了异步的通信协议, 数据传递是单向的, 发送者向消息队列中不断写入消息, 消费者不断从消息队列中读取消息. 发布者与消费者不用同时与队列交互, 队列可以持久的保存消息.

扫描二维码关注公众号,回复: 4059501 查看本文章

消息队列的组成

消息队列大体可分成三部分: 消息生产者(Producer) --> 消息代理(Broker) --> 消息消费者(Consumer), 其中的"-->"表示消息的流动方向, 消息在消息队列中只可以单向流动. 其中的消息代理是由交换机, 绑定和队列所组成的, 具体可见下图.

消息队列大致组成

消息队列的作用

1. 降低耦合, 减少服务间的依赖, 连接不同平台或语言

2. 将同步变为异步, 增加系统吞吐量, 缩短响应时间

3. 削弱峰值, 抗住压力, 防止奔溃

4. 便于服务实现分布式与横向扩展

谈到消息队列就不得不提AMQP协议, 许多市面上流行的消息队列都是围绕该协议来构建的, 该协议为异步消息处理提供了一种标准.

AMQP协议

AMQP即高级消息队列协议(Advanced Message Queuing Protocol), 其是一种开源的, 基于TCP的应用层协议. 该协议的设计目的是为摆脱商业消息队列高昂的授权费用, 并为不同消息队列产品提供统一的接口标准.

AMQP指出消息队列主要由消息生产者, 消息代理(交换机, 绑定, 队列), 消息消费者所构成. 消息由消息生产者创建, 经消息代理流向消息消费者. 下面将介绍消息的构成, 消息生产者与消费者, 消息代理, 消息持久化与消息确认机制.

1. 消息的构成

消息由消息属性与有效载荷(Payload)两部分组成.

1) 有效载荷

有效载荷也就是要传输的数据, 消息代理(下面介绍)不会对其进行检查或修改.

2) 消息属性

消息属性类似于HTTP协议的头部, 其包含交换机名字(用于决定消息被发送到的交换机), 内容类型, 内容编码, 路由键, 投递模式(是否持久化), 消息优先权(优先队列), 消息时间戳, 消息有效期等字段.

3) 消息持久化

消息持久化即断电后消息是否丢失. 某条消息是否持久化是通过消息属性中的"投递模式"字段来决定的. 如果不设置该字段, 而只是将消息发送给持久化交换机或是路由到持久化队列是不会使该消息具有持久化特质的.

2. 消息生产者

根据有效载荷创建消息(设置消息属性), 并将消息交给消息代理.

3. 消息消费者

从消息代理那儿接收消息中的有效载荷, 注意其只关心有效载荷而不接收消息属性.

AMQP支持主动推送消息给应用(push), 以及应用主动获取消息(pull). 采用push时, 应用需显示的表明其对哪个队列的消息感兴趣, 我们称之为应用订阅了某队列, 或注册了某消费者.

4. 消息代理

消息代理(Broker)一般由交换机, 绑定和队列所构成. 利用虚拟化技术, 还可以产生虚拟主机这一层级.

1) 连接与信道

消息代理一般可与发布者和消费者保持长连接, 该连接基于TCP, 支持TLS(SSL)加密.

为解决生产者或消费者大量并发连接消息代理的问题, 引入了信道的概念. 信道是建立在TCP连接内的虚拟连接, 通过多路复用技术, 多个信道可共享一个TCP连接, 从而避免大量建立TCP连接时资源的消耗. 同个连接下的不同信道间传输数据时互不影响, 因此建议多个线程或进程共享一个连接, 但每个线程或进程独享连接下的一个信道.

2) 虚拟主机

消息队列通过虚拟化技术, 可以在一个消息代理上实现多个隔离的环境, 使每个环境都有独立的用户组, 交换机和队列, 该环境被称为虚拟主机(Virtual Host). 这个类似于操作系统中物理机和虚拟机, 以及编程语言中命名空间的概念.

消息队列详细组成

3) 交换机

生产者实际将消息发送到消息代理的交换机(Exchange)上, 交换机接收并排序这些消息, 再按一定规则将它们分别转发(路由)到对应队列上. 这里的一定规则主要取决于两个条件, 其一是交换机类型, 其二是消息所携带的路由键属性, 具体例子在"交换机类型"那儿介绍.

消息代理被创建后会自动声明一个默认交换机, 其类型为直连交换机(下面介绍), 每个新建的队列都会自动绑定到该交换机上, 绑定的路由键与队列名是一致的.

[1] 路由键与绑定键

路由键存在于消息中, 用于告知交换机将其转发(路由)到哪条队列上. 路由键形式为字符串, 可被"."分隔成多个单词, 一个消息仅能有一个路由键.

绑定键存在于队列中, 用于告知交换机自己接收哪种类型的消息. 绑定键形式为字符串, 可被"."分隔成多个单词, 一条队列能有多个绑定键.

[2] 交换机类型

消息具体转发规则是由交换机类型决定的, 交换机分为四种类型: 直连交换机, 主题交换机, 扇形交换机和头交换机. 主要关注前三个, 最后一个不常用.

直连交换机 (direct, 类似网络里的"单播"): 一个携带"XX"路由键的消息被发给直连交换机后, 直连交换机会将其转发给所有具有"XX"绑定键的队列.

扇形交换机 (fanout, 类似网络里的"广播"): 交换机将接收到的消息转发给绑定到该交换机上的所有队列, 不管路由键与绑定键是否匹配.

主题交换机 (topic, 类似网络里的"组播"): 类似于直连交换机, 但匹配规则不是"XX"路由键与"XX"绑定键完全匹配, 而是只要符合类似正则匹配即可. 具体规则如下:  绑定键中的"#"可替换零个或多个单词, "*"只替换一个单词(注意是单词, 不是字符). 当绑定键仅为"#"时, 就成了扇形交换, 而当绑定键中没有使用"#"或"*"时, 就成了直连交换.

举个例子, 队列1的绑定键为"*.red.*", 队列2的绑定键为"monkey.#"和"*.*.fat":
路由键为"monkey.red.thin"的消息会转发给队列1与队列2;
路由键为"monkey.blue.fat"的消息会转发给队列2, 且仅会被转发一次, 虽然匹配中了两次队列2的绑定键;
路由键为"red"的消息因没有匹配到合适队列, 会被丢弃.

[3] 交换机状态

分为"持久"和"暂存"两种状态, 区别在于"持久交换机"在消息代理重启后依旧存在, "暂存交换机"在代理重启后需被重新声明.

4) 队列

队列(Queue)用于在消费者消费消息前存储从交换机那儿接收到的消息.

队列状态和交换机一样, 也分为"持久"和"暂存"两种. "持久队列"在消息队列重启后依旧存在, 而"暂存队列"则需被重新声明.

5) 绑定

将队列挂到指定交换机下的操作被称为绑定(binding), 交换机将消息转发给队列时所参照的规则被称为绑定规则. 绑定连接了交换机和队列两个实体, 其依赖于消息中的路由键与队列上的绑定键.

5. 消息确认机制

为确保消息不丢失, 消息代理支持两种确认模式: 其一是消息转发给消费者后立即自动确认, 即自动确认模式. 其二是等待消费者自己发送确认, 即手动确认模式. 无论是哪种模式, 消费者都需要告诉消息代理自己已收到该消息, 之后消息代理便可自由删除该消息.

当一名消费者die后(如信道或连接被关闭, 底层TCP丢失等), 消息代理会将已发送给该消费者且未收到确认的消息重新放入队列中, 如果有其他消费者在线, 这些消息会立即转发给他们处理.

注意确认消息必须在相同的信道上发送, 请不要使用新的或其它信道. 如果你开启了手动消息确认, 但在消息处理后却一直忘记确认, 那么消息会不停的重发, 这不仅在一定程度上造成消息混乱, 而且消息队列还会不断吃内存, 情况将变得很危险.

6. 消息队列工作流程

简单来说工作流程就是生产者将有效载荷(真正的数据)封装成消息包, 然后发送给消息代理, 而消息代理依据一定规则将消息转发(路由)给消费者.

更具体来说工作流程则是生产者将消息传递给消息代理上的交换机, 交换机根据消息里的路由键以及自己的类型, 将该消息转发(路由)到对应队列上, 最后消费者会从其关心(订阅)的队列中取得有效载荷并处理.

为应对上面流程中出现的异常, AMQP通过消息确认机制来保证送达, 只有当消费者向消息代理发送"收到确认(ACK)"后, 消息代理才会从队列中删除该消息.

总结

AMQP协议的核心是消息代理, 消息代理则可简单认为是由交换机, 队列, 绑定所组成的.

AMQP所指导的消息队列已为我们实现了三大主体(交换机, 队列以及如何绑定), 但转发(路由)方案需要我们自己实现, 路由算法由交换机类型和绑定规则共同决定.

文中如有不当之处, 还望包容和指出, 感谢.

猜你喜欢

转载自blog.csdn.net/jinixin/article/details/83552185