https://www.rabbitmq.com/getstarted.html
MQ是消息中间件,常见的有RabbitMQ,Kafka,RocketMQ,activeMQ 等,用于分布式系统中。作用有三点:
RabbitMQ 整体上是一个生产者,消费者模型,主要负责接收,存储和转发消息。
产生消息的应用,能够传递消息到消息中间件的应用。
消息传递的中间载体,即我们今天的主角 RabbitMQ。
接收并处理消息的应用。从消息中间件获取消息并处理。
1>应用程序与 Rabbit 之间建立连接的管理器,程序代码中使用。
2>主要表示生产者/消费者与消息代理(Rabbitmq)建立的 TCP 连接,AMQP 的连接通常是长连接,AMQP 支持鉴权和 TLS,以确保 “连接” 的数据安全。
3>当我们的生产者或消费者不再需要连接到消息中间件的的时候,需要优雅的释放掉它们与消息中间件 TCP 连接,而不是直接将 TCP 连接关闭。
1、消息推送使用的通道,可以把通道理解成共享一个 TCP 连接的多个轻量化连接。
2、一个特定通道上的通讯与其他通道上的通讯是完全隔离的,因此每个 AMQP 方法都需要携带一个通道号。
3、AMQP 通过 “信道”(Channel)构建 “连接” 的多路复用:
1)“信道” 共享 “连接”。
2)“信道” 相互独立。
3)任何 AMQP 通信,都属于 “信道” 层面的通信。
1、每个 Rabbit 都能创建很多 vhost,我们称之为虚拟主机,每个虚拟主机其实都是 mini 版的 RabbitMQ,拥有自己的队列,交换器和绑定,拥有自己的权限机制。
2、AMQP 以 “虚拟主机”(virtual host)形式,于消息代理中提供 “隔离” 的运行环境。默认 “虚拟主机”:/。
3、多个 vhost 是隔离的,多个 vhost 无法通讯,并且不用担心命名冲突(队列和交换器和绑定),实现了多层分离;
4、创建用户的时候必须指定 vhost;
5、vhost 操作
创建:rabbitmqctl addvhost[vhostname]
删除:rabbitmqctl deletevhost[vhostname]
查看:rabbitmqctl list_vhosts
6、最主要的目的是考虑到不同的分布式系统下面,如果我们有类似的业务场景,相应的可能会有相同名称的 exchange 和 queue,有了虚拟主机的概念就可以轻松区分了。
最直接了当的认证方式,谁可以使用当前的消息中间件。
交换器负责接收来自生产者的消息,将消息路由到 0 到多个队列。交换器的关键属性,主要包括:
1)名称(Name)。
2)类型:消息路由的规则,即由 “交换器类型” 和绑定规则,共同决定。
3)持久化(Durability):消息代理重启后,交换机是否还存在。交换机可以有两个状态:持久(durable)、暂存(transient)。持久化的交换机会在消息中间件(broker)重启后依旧存在,而暂存的交换机则不会(它们需要在消息中间件再次上线后重新被声明)。然而并不是所有的应用场景都需要持久化的交换机。
4)自动删除(Auto-delete):当所有与之绑定的消息队列都完成了对此交换机的使用后,是否自动删掉它。
1、队列以绑定键 B 绑定到 direct 交换器。
2、消息(路由键 R)发送到 direct 交换器,若 B == R,消息即进入队列。
3、AMQP 提供了 “默认交换器”:类型为 direct,名称为空字符串。任何的队列被创建时,即以队列名称作为绑定键,绑定到 “默认交换器”。
fanout 交换器将消息路由到所有绑定的队列。类似于 “广播”。
topic 交换器与 direct 交换器类似,基于消息路由键与队列绑定键进行匹配。区别在于,topic 交换器支持 “通配符” 形式的 “绑定键”:
1)“键” 以 . 划分成多个词
2)* 匹配任意 1 个词
3)# 匹配 0 到多个词:demo.rmq.example_1 与 *.rmq.example_1 和 demo.# 匹配。
*(星号) 用来表示一个单词 (必须出现的)
#(井号) 用来表示任意数量(零个或多个)单词
通配的绑定键是跟队列进行绑定的,举个小例子
队列Q1 绑定键为 *.TT.*队列Q2绑定键为 TT.#
如果一条消息携带的路由键为 A.TT.B,那么队列Q1将会收到;
如果一条消息携带的路由键为TT.AA.BB,那么队列Q2将会收到;
与 direct 交换器类似,区别在于,header不依赖于消息路由键与队列绑定键的匹配,而是依赖于消息和绑定的 “headers” 匹配。
具体的匹配规则,依赖绑定的 “headers” 支持 x-match 属性:
1)all:默认值,当且仅当,消息 “headers” 与绑定 “headers”,全部 K-V 匹配
2)any:消息 “headers” 中任意 K-V,都能够与绑定 “headers” 匹配
3)“headers” 中,若以 x- 作为前缀,则不参与匹配计算。
队列接收来自交换器分发的消息,供消费者读取。队列的关键属性,包括:
1)队列名称
2)持久化:消息中间件重启后,队列是否依旧存在。持久化队列(Durable queues)会被存储在磁盘上,当消息中间件(broker)重启之后,它依旧存在。没有被持久化的队列称作暂存队列(Transient queues)。这里需要注意队列的持久化和它存储的未被消费消息的持久化是 2 个概念,队列的持久化并不会使存储的消息持久化。假如消息中间件(broker)重启之后,持久化队列会被重新声明,但它里面存储的消息只有设置过持久化的消息才能被重新恢复。
3)自动删除:队列没有 “订阅” 的消费者时,是否自动删除。
4)专用队列(Exclusive):可以这样理解当创建这个队列的 Connection 关闭后队列即被删除,不存在其它的使用可能性。
消息队列在声明(declare)后才能被使用。如果一个队列尚不存在,声明一个队列会创建它。如果声明的队列已经存在,并且属性完全相同,那么此次声明不会对原有队列产生任何影响。如果声明中的属性(名称除外)与已存在队列的属性有差异,那么则会申明失败。
消息的关键属性包括:
1)路由键:交换器路由消息的 “依据”。
2)投递模式:消息是否 “持久化”(消息代理重启后,交换器是否仍然 “存在”)。
3)Content-Type / Content-Encoding:通常作为 “载荷” 数据结构的标识。(“载荷” 即为消息传递的数据,其数据结构由应用决定,AMQP 保持透明,仅将其作为字节数组)
4)消息头(headers):消息的附加属性,K-V 结构。
为了达成消息的 “持久化”,消息、交换器、队列,必须全部 “持久化”。
路由关键字,交换机 exchange 的路由规则利用这个关键字进行消息投递到消息队列。(路由键长度不能超过 255 个字节)
用于把生成者的数据分配到交换器上。
用于把交换器的消息绑定到队列上。
队列允许 “绑定” 到交换器,针对部分 “交换器类型”,绑定需要提供 “绑定键”(亦称为 “路由键”,区分于消息的 “路由键” 属性)。
BindingKey:用于把交换器的消息绑定到队列上。
怎么保证 Rabbit 在重启的时候不丢失呢?答案就是消息持久化。
当你把消息发送到 Rabbit 服务器的时候,你需要选择你是否要进行持久化,但这并不能保证 Rabbit 能从崩溃中恢复,想要 Rabbit 消息能恢复必须满足以下条件:
1)投递消息的时候 durable 设置为 true,消息持久化,代码:
//参数2设置为true持久化;
channel.queueDeclare(x, true, false, false, null),
2)设置投递模式 deliveryMode 设置为 2(持久),代码:
//参数 3 设置为存储纯文本到磁盘;
channel.basicPublish(x, x, MessageProperties.PERSISTENTTEXTPLAIN,x),
3)消息已经到达持久化交换器上;
4)消息已经到达持久化的队列;
原理:Rabbit 会将你的持久化消息写入磁盘上的持久化日志文件,等消息被消费之后,Rabbit 会把这条消息标识为等待垃圾回收。
缺点:消息持久化的优点显而易见,但缺点也很明显,那就是性能,因为要写入硬盘要比写入内存性能较低很多,从而降低了服务器的吞吐量。