什么是消息队列
“消息队列(Message Queue)”是在消息的传输过程中保存消息的容器。在消息队列中,通常有生产者和消费者两个角色。
消息队列优缺点
优点:
- 解耦
- 异步
- 削峰
缺点:
- 增加系统复杂度
- 降低系统可用性
- 一致性问题
RabbitMQ特点
衡量一个MQ的指标,主要有三个方面:服务性能、数据存储、集群架构
- 开源、性能优秀、稳定性保障
- 提供可靠性消息投递模式(confirm)、返回模式(return)
- 灵活的分发消息策略。分发消息策略有:简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式。
- 集群模式丰富、表达式配置、HA(高可用)模式、镜像队列模型
- 可视化管理界面。RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker。
- 与SpringAMQP完美的整合、扩展性变得更强、API丰富
RabbitMQ AMQP协议模型
AMQP协议基本概念
Connection
-
对应底层一个AMQP-Client到RabbitMQ-Broker的一个TCP连接。
-
这边要考虑两个端点问题,在TCP连接建立完成后,连接的目标Broker就已经确定是集群中的一台了,由于是长连接,除非断连重建,否则对端节点不可变。
-
所以从这里可以看出RabbitMQ相比Pulsar、RocketMQ不一样的地方在于,其是一种服务端寻址模型,以Client的视角来看,想要连接任意Exchange、Queue,只要连上任意一台Broker就行。
RabbitMQ 基本概念
-
Publisher,数据的发送方。
-
Message,消息体,是AMQP所操纵的基本单位,它由Producer产生,经过Broker被Consumer所消费。 它的基本结构有两部分: Header和Body。 Header是由Producer添加上的各种属性的集合,这些属性有控制Message是否可被缓存,接收的Queue是哪个,优先级是多少等。 Body是真正需要传送的数据,它是对Broker不可见的二进制数据流,在传输过程中不应该受到影响。
-
Broker ,AMQP的服务端称为Broker。其实Broker就是接收和分发消息的应用,也就是说RabbitMQ Server就是Message Broker。
-
Virtual Host,虚拟主机,一批交换器(Exchange),消息队列(Queue)和相关对象。 虚拟主机是共享相同身份认证和加密环境的独立服务器域。同时一个Broker里可以开设多个vhost,用作不同用户的权限分离。
-
Exchange 消息交换机,它指定消息按什么规则,路由到哪个队列。
-
Binding,绑定,消息队列与交换器直接关联的,它的作用就是把Exchange和Queue按照路由规则绑定起来。
-
Routing Key,路由关键字,Exchange根据这个关键字进行消息投递。
-
Queue,消息队列载体,每个消息都会被投入到一个或多个队列。
-
Connection,连接,一个网络连接,比如TCP/IP套接字连接。Channel是建立在Connection之上的,一个Connection可以建立多个Channel。
-
Channel,信道,多路复用连接中的一条独立的双向数据流通道,为会话提供物理传输介质。 Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程, 通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。 Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。在客户端的每个连接里,可建立多个Channel,每个Channel代表一个会话任务。
-
Consumer,数据的接收方。
RabbitMQ 消息结构
# publishInfo
exchange: amq.direct
immediate: false
mandatory: false
routingKey: test
# headerBody
bodySize: 1024
properties:
- contentType:
- encoding:
- deliveryMode:
- priority:
- correlationId:
- replyTo:
- expiration:
- messageId:
- timestamp:
- type:
- userId:
- appId:
- clusterId:
- headers: {}
# contentBody
二进制消息体bytes
每个消息分为三个部分,在网络层面即三个独立数据帧:
-
PublishInfo: 消息路由声明信息,可以联想成写电子邮件时填写的目标邮箱、是否接收回执等前置声明。
-
HeaderBody: 消息头部,用于存储RabbitMQ自身事先定义的声明,可以联想层HTTP协议的Header一样,此处可以放置一些对业务透明的上下文信息用于提供某种功能,比如分布式链路追踪的TraceId。
-
ContentBody: 消息体,无差别二进制数据块,服务端不感知其是否压缩、是否加密等,只进行透明的存储和读取投递。
RabbitMQ 工作模式
1、 direct。消息的routing key需要精确匹配binding key。
当生产者(P)发送的消息Rotuing key=booking时,发现Queue1和Queue2都符合,就会将消息传送给这两个队列,如果以Rotuing key=create或Rotuing key=confirm发送消息时,这时消息只会被推送到Queue2队列中,其他Routing Key的消息将会被丢弃。
注意RabbitMQ有一个默认Exchange,类型是direct,没有办法绑定一个queue到这个exchange,但是通过指定routing key,可以发送消息到同名的queue。
2、 fanout。这个类型的exchange会发送消息到所有绑定在上面的queue,routing key和binding key不起作用。
生产者(P)生产消息1将消息1推送到Exchange,Exchange将消息推送到所有与它绑定Queue,最后两个消费者都会收到消息。
3、topic。Binding key可以使用*和#来对routing key进行模糊匹配。
点号“. ”分隔的每一段字符串称为一个单词,如“quick.orange.rabbit”包含3个单词quick,orange,rabbit。点分割的部分可以为空,比如bindingkey=.的可以匹配routingkey是”.”的消息,但是bindingkey=“”无法匹配routingkey是空的消息。可以简单理解,先将routingkey按照stringsplit方法用”.”分割,保留空白字符,表示匹配分割后的一个位置,#表示匹配0…n个位置。其余字符需要精确匹配。 表示一个单词,#表示0或多个单词。 binding key使用“”与“#”来模糊匹配routing key。routing key中的“*”或“#”当做普通字符处理。
当生产者发送消息Routing Key=F.C.E时,只会被路由到Queue1中,如果Routing Key=A.C.E这时候会被同时路由到Queue1和Queue2中,如果Routing Key=A.F.B时,这里只会发送一条消息到Queue2中
Will “*” binding catch a message sent with an empty routing key? 不会
Will “#.*” catch a message with a string “..” as a key? Will it catch a message with a single word key? 都会
How different is “a.*.#” from “a.#”? 前者routingkey要有”a.”,后者只要有”a”就能匹配。
- headers。headers交换器允许你匹配AMQP消息的header而非路由键,除此之外,和direct交换器完全一样,但是性能会差很多, 因此并不太实用,而且几乎再也用不到了。使用方法:在绑定的时候提供一组键值对,如果消息的header(也是一组键值对)和其完全匹配,则路由消息到队列。