RabbitMQ基础入门
hm
RabbitMQ 与 AMQP 遵循相同的模型架构,其架构示例图如下
# 1 重要概念
# 1.1 Publisher
消息生产者,就是投递消息的程序
发布者 (或称为生产者) 负责生产消息并将其投递到指定的交换器上。
# 1.2 Message
消息由消息头和消息体组成,消息头用于存储与消息相关的元数据:如目标交换器的名字 (exchange_name)路由键 (RountingKey)和其他可选配置 (properties) 信息。消息体为实际需要传递的数据。
# 1.3 Exchange
交换器负责接收来自生产者的消息,并将消息路由到一个或者多个队列中,如果路由不到,则返回给生产者或者直接丢弃,这取决于交换器的 mandatory 属性:
- 当 mandatory 为 true 时:如果交换器无法根据自身类型和路由键找到一个符合条件的队列,则会将该消息返回给生产者;
- 当 mandatory 为 false 时:如果交换器无法根据自身类型和路由键找到一个符合条件的队列,则会直接丢弃该消息。
# 1.4 BindingKey
交换器与队列通过 BindingKey 建立绑定关系。
# 1.5 Routingkey
基于交换器类型的规则相匹配时,消息被路由到对应的队列中
生产者将消息发给交换器的时候,一般会指定一个 RountingKey,用来指定这个消息的路由规则,当 RountingKey 与 BindingKey
# 1.6 Queue
消息队列载体,每个消息都会被投入到一个或多个队列。
用于存储路由过来的消息,多个消费者可以订阅同一个消息队列,此时队列会将收到的消息将以轮询 (round-robin) 的方式分发给所有消费者,即每条消息只会发送给一个消费者,不会出现一条消息被多个消费者重复消费的情况。
# 1.7 Consumer
消息消费者,就是接受消息的程序
消费者订阅感兴趣的队列,并负责消费存储在队列中的消息。为了保证消息能够从队列可靠地到达消费者,RabbitMQ 提供了消息确认机制 (messageacknowledgement),并通过 autoAck 参数来进行控制
- 当 autoAck 为 true 时:此时消息发送出去 (写入TCP套接字) 后就认为消费成功,而不管消费者是否真正消费到这些消息。当 TCP 连接或 channel 因意外而关闭,或者消费者在消费过程之中意外宕机时,对应的消息就丢失。因此这种模式可以提高吞吐量,但会存在数据丢失的风险。
- 当 autoAck 为 false 时:需要用户在数据处理完成后进行手动确认,只有用户手动确认完成后,RabbitMQ 才认为这条消息已经被成功处理,这可以保证数据的可靠性投递,但会降低系统的吞吐量。
# 1.8 Connection
用于传递消息的 TCP 连接。
# 1.9 Channel
消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
RabbitMQ 采用类似 NIO (非阻塞式 IO ) 的设计,通过 Channel 来复用 TCP 连接,并确保每个 Channel的隔离性,就像是拥有独立的 Connection 连接。当数据流量不是很大时,采用连接复用技术可以避免创建过多的 TCP 连接而导致昂贵的性能开销。
# 1.10 Virtual Host
虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离
RabbitMQ 通过虚拟主机来实现逻辑分组和资源隔离,一个虚拟主机就是一个小型的 RabbitMQ服务器,拥有独立的队列、交换器和绑定关系。用户可以按照不同业务场景建立不同的虚拟主机,虚拟主机之间是完全独立的,你无法将 vhost1 上的交换器与vhost2 上的队列进行绑定,这可以极大的保证业务之间的隔离性和数据安全,默认的虚拟主机名为 /
。
# 1.11Broker
简单来说就是消息队列服务器实体。
# 2 RabbitMQ安装
参考:https://baiyp.ren/RabbitMQ%E5%AE%89%E8%A3%85.html (opens new window)
# 3 如何使用RabbitMQ发送消息?
exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
- 客户端连接到消息队列服务器,打开一个channel。
- 客户端声明一个exchange,并设置相关属性。
- 客户端声明一个queue,并设置相关属性。
- 客户端使用routing key,在exchange和queue之间建立好绑定关系。
- 客户端投递消息到exchange。
# 4 消息怎么路由?
从概念上来说,消息路由必须有三部分:交换器、路由、绑定
生产者把消息发布到交换器上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。
消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定,通过队列路由键,可以把队列绑定到交换器上。
消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则),如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进入 “黑洞”。
# 5 常用交换器有哪些?
交换器是消息被发送的 AMQP 实体,交换器拿到消息然后把它路由给0或多个队列,路由算法基于交换器的类型和 bindings
常用的交换器主要分为一下三种:
Exchange type(交换器类型) | Default pre-declared names(预声明默认名称) |
---|---|
Direct exchange(直连交换器) | (Empty string) and amq.direct |
Fanout exchange(扇形交换器) | amq.fanout |
Topic exchange(主题交换器) | amq.topic |
Headers exchange(头信息交换器) | amq.match (and amq.headers in RabbitMQ) |
# 5.1 直连交换机
如果路由键完全匹配,消息就被投递到相应的队列
直连型交换机(direct exchange)是根据消息携带的路由键(routing key)将消息投递给对应绑定键的队列。
直连交换机是一种带路由功能的交换机,一个队列会和一个交换机绑定,除此之外再绑定一个routing_key,当消息被发送的时候,需要指定一个binding_key,这个消息被送达交换机的时候,就会被这个交换机送到指定的队列里面去,同样的一个binding_key也是支持应用到多个队列中的。
直连型交换机图例:
当生产者(P)发送消息时 Rotuing key=booking 时,这时候将消息传送给 Exchange,Exchange 获取到生产者发送过来消息后,会根据自身的规则进行与匹配相应的 Queue,这时发现 Queue1 和 Queue2 都符合,就会将消息传送给这两个队列。
如果我们以 Rotuing key=create 和 Rotuing key=confirm 发送消息时,这时消息只会被推送到 Queue2 队列中,其他 Routing Key 的消息将会被丢弃。
# 5.2 扇型交换机
如果交换器收到消息,将会广播到所有绑定的队列上
扇型交换机(fanout exchange)将消息路由给绑定到它身上的所有队列,而不理会绑定的路由键。如果 N 个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的 N 个队列。扇型用来交换机处理消息的广播路由(broadcast routing)。
因为扇型交换机投递消息的拷贝到所有绑定到它的队列,所以他的应用案例都极其相似:
- 大规模多用户在线(MMO)游戏可以使用它来处理排行榜更新等全局事件
- 体育新闻网站可以用它来近乎实时地将比分更新分发给移动客户端
- 分发系统使用它来广播各种状态和配置更新
- 在群聊的时候,它被用来分发消息给参与群聊的用户。(AMQP 没有内置 presence 的概念,因此 XMPP 可能会是个更好的选择)
扇型交换机图例:
上图所示,生产者(P)生产消息 1 将消息 1 推送到 Exchange,由于 Exchange Type=fanout 这时候会遵循 fanout 的规则将消息推送到所有与它绑定 Queue,也就是图上的两个 Queue 最后两个消费者消费。
# 5.3 主题交换机
可以使来自不同源头的消息能够到达同一个队列,使用topic交换器时,可以使用通配符
基于消息的 routing key 与绑定到该交换器的队列的 pattern 进行匹配,路由消息到一个或多个队列,常用于复杂的发布/订阅场景,当出现多消费者/应用的场景,消费者选择性地接收消息时,应该考虑使用 topic exchange
前面提到的 direct 规则是严格意义上的匹配,换言之 Routing Key 必须与 Binding Key 相匹配的时候才将消息传送给 Queue,而Topic 的路由规则是一种模糊匹配,可以通过通配符满足一部分规则就可以传送。
# 5.3.1 约束条件
- binding key 中可以存在两种特殊字符 “” 与“#”,用于做模糊匹配,其中 “” 用于匹配一个单词,“#”用于匹配多个单词(可以是零个)
- routing key 为一个句点号 “.” 分隔的字符串(我们将被句点号 “. ” 分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit” binding key 与 routing key 一样也是句点号 “.” 分隔的字符串
主题交换机图例:
当生产者发送消息 Routing Key=F.C.E 的时候,这时候只满足 Queue1,所以会被路由到 Queue 中,如果 Routing Key=A.C.E 这时候会被同是路由到 Queue1 和 Queue2 中,如果 Routing Key=A.F.B 时,这里只会发送一条消息到 Queue2 中。
主题交换机拥有非常广泛的用户案例。无论何时,当一个问题涉及到那些想要有针对性的选择需要接收消息的 多消费者 / 多应用(multiple consumers/applications) 的时候,主题交换机都可以被列入考虑范围。
# 5.3.2 使用案例
- 分发有关于特定地理位置的数据,例如销售点
- 由多个工作者(workers)完成的后台任务,每个工作者负责处理某些特定的任务
- 股票价格更新(以及其他类型的金融数据更新)
- 涉及到分类或者标签的新闻更新(例如,针对特定的运动项目或者队伍)
- 云端的不同种类服务的协调
- 分布式架构 / 基于系统的软件封装,其中每个构建者仅能处理一个特定的架构或者系统。
# 5.4 头交换机(不常用)
headers 类型的 Exchange 不依赖于 routing key 与 binding key 的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配。
头交换机可以视为直连交换机的另一种表现形式。但直连交换机的路由键必须是一个字符串,而头属性值则没有这个约束,它们甚至可以是整数或者哈希值(字典)等。灵活性更强(但实际上我们很少用到头交换机)。工作流程:
- 绑定一个队列到头交换机上时,会同时绑定多个用于匹配的头(header)。
- 传来的消息会携带header,以及会有一个 “x-match” 参数。当 “x-match” 设置为 “any” 时,消息头的任意一个值被匹配就可以满足条件,而当 “x-match” 设置为 “all” 的时候,就需要消息头的所有值都匹配成功。
# 5.5 交换机小结
类型名称 | 路由规则 |
---|---|
Default | 自动命名的直交换机 |
Direct | 把消息路由到BindingKey和RoutingKey完全匹配的队列中,Routing Key==Binding Key,严格匹配 |
Fanout | 发送到该交换机的消息都会路由到与该交换机绑定的所有队列上,可以用来做广播 |
Topic | topic和direct类似,也是将消息发送到RoutingKey和BindingKey相匹配的队列中,只不过可以模糊匹配 |
Headers | 根据发送的消息内容中的 headers 属性进行匹配,性能差,基本不会使用 |