routing key
生产者在将消息发送给 Exchange 的时候,一般会指定一个 routing key,来指定这
个消息的路由规则,而这个 routing key 需要与 Exchange Type 及 binding key 联合使
用才能最终生效。
Binding
RabbitMQ 中通过
Binding 将 Exchange 与 Queue 关联起来,这样 RabbitMQ 就知道
如何正确地将消息路由到指定的 Queue 了。
Binding key
在绑定(Binding)Exchange
与 Queue 的同时,一般会指定一个 binding key;消费
者将消息发送给 Exchange 时,一般会指定一个 routing key;当 binding key 与 routing
key 相匹配时,消息将会被路由到对应的 Queue 中。
Exchange Types
fanout
fanout 类型的 Exchange
路由规则非常简单,它会把所有发送到该 Exchange 的消息
路由到所有与它绑定的 Queue 中。
direct
direct 类型的 Exchange
路由规则也很简单,它会把消息路由到那些 binding key 与
routing key 完全匹配的
Queue 中。
topic
前面讲到 direct 类型的
Exchange 路由规则是完全匹配 binding key 与 routing
key,但这种严格的匹配方式在很多情况下不能满足实际业务需求。topic 类型的 Exchange
在匹配规则上进行了扩展,它与 direct 类型的 Exchage 相似,也是将消息路由到 binding
key 与 routing key 相匹配的 Queue 中,但这里的匹配规则有些不同,它约定:
routing key 为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一
段独立的字符串称为一个单词),如“stock.usd.nyse ”、“nyse.vmw”、
“quick.orange.rabbit”
binding key 与 routing key 一样也是句点号“. ”分隔的字符串
binding key 中可以存在两种特殊字符“”与“#”,用于做模糊匹配,其中“”用于匹
配一个单词,“#”用于匹配多个单词(可以是零个)
工作队列-消息轮询分发-消费者收到的消息数量平均分配,单位时间内消息处理
速度加快,提高了吞吐量。
案例
02 : 工作 模式 队列- - 消息公平分发
(fair dispatc h)
在案例 01 中对于消息分发采用的是默认轮询分发,消息应答采用的自动应答模式,这
是因为当消息进入队列,RabbitMQ 就会分派消息。它不看消费者为应答的数目,只是盲目
的将第 n 条消息发给第 n 个消费者。
为了解决这个问题,我们使用 basicQos(prefetchCount = 1)方法,来限制 RabbitMQ
只发不超过 1 条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发
送。
案例
03 : 消息的发布与订阅 模式队列 (Publish/Subscribe)
对于微信公众号,相信每个人都订阅过,当公众号发送新的消息后,对于订阅过该公众
号的所有用户均可以收到消息,这个场景大家都能明白,同样对于 RabbitMQ 消息的处理也支持这种消息处理,当生产者把消息投送出去后,不同的消费者均可以对该消息进行消费,
而不是消息被一个消费者消费后就立即从队列中删除,对于这种消息处理,我们通常称之为
消息的发布与订阅模式,凡是消费者订阅了该消息,均能够收到对应消息进行处理,比较常
见的如用户注册操作。模型图如下:
从图中看到:
1.消息产生后不是直接投送到队列中,而是将消息先投送给 Exchange 交换机,然后消
息经过 Exchange 交换机投递到相关队列
2.多个消费者消费的不再是同一个队列,而是每个消费者消费属于自己的队列。
Routing-路由模式队列
案例
04 : 路由 模式队列( ( Routing) )
通过案例 03,可以看到,生产者将消息投送给交换机后,消息经交换机分发到不同的队
列即:交换机收到消息,默认对于绑定到每个交换机的队列均会接收到交换机分发的消息,
对于案例 03 的交换机的消息分发
Exchange Types 为 fanout 类型,通常在真正项目开发
时会遇到这种情况:在对项目信息输出日志进行收集时,会把日志(error warning,info)
分类进行输出,这时通过 Exchange Types 中的 direct 类型就可以实现,针对不同的消
息,在对消息进行消费时,通过 Exchange types 以及 Routing key 设置的规则 ,便可
以将不同消息路由到不同的队列中然后交给不同消费者进行消费操作。模型图如下:
从图中可以看出:
-
生产者产生的消息投给交换机
-
交换机投送消息时的
Exchange Types 为 direct 类型 -
消息通过定义的 Routing
Key 被路由到指定的队列进行后续消费
总结
从结果可以看出生产者发送了多条设置了路由规则的消息,消费者可以根据具体的路由
规则消费对应队列中的消息,而不是所有消费者都可以消费所有消息了。
问题:生产者产生的消息如果场景需求过多需要设置很多路由规则,可不可以减少?
解决:采用 topic 主题模式。
Topics-主题模式队列
案例 0 05 5 :主题模式队列(
(T T opic)
通过案例 04 看到消息通过交换机
Exchange Type 以及 Routing Key 规则,可以将消
息路由到指定的队列,也符合在工作中的场景去使用的一种方式,对于 RabbitMq 除了
direct 模式外,Mq 同样还提供了 topics 主题模式来对消息进行匹配路由,比如在项目
开发中,拿商品模块来说,对于商品的查询功能在对商品进行查询时我们将查询消息路由到
查询对应队列,而对于商品的添加、更新、删除等操作我们统一路由到另外一个队列来进行
处理,此时采用 direct 模式可以实现,但对于维护的队列可能就不太容易进行维护,如果
涉及模块很多,此时对应队列数量就很多,此时我们就可以通过 topic 主题模式来对消息
路由时进行匹配,通过指定的匹配模式将消息路由到匹配到的队列中进行后续处理。对于
routing key 匹配模式定义规则举例如下:
•
routing key 为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一
段 独 立 的 字 符 串 称 为 一 个 单 词 ), 如 “
stock.usd.nyse ” 、 “ nyse.vmw ” 、
“quick.orange.rabbit”
•
routing key 中可以存在两种特殊字符“”与“#”,用于做模糊匹配,其中“”用于匹
配一个单词,“#”用于匹配多个单词(可以是零个)
例如:
以上图中的配置为例:
routingKey=”quick.orange.rabbit”的消息会同时路由到 Q1 与 Q2,
routingKey=”lazy.orange.fox”的消息会路由到 Q1,Q2,
routingKey=”lazy.brown.fox”的消息会路由到 Q2,
routingKey=”lazy.pink.rabbit”的消息会路由到 Q2;
routingKey=”quick.brown.fox”、
routingKey=”orange”、
routingKey=”quick.orange.male.rabbit”的消息将会被丢弃,因为它们没有匹配
任何 bindingKey。
从结果可以看出生产者发送了多条设置了路由匹配规则(主题)的消息,根据不同的路
由匹配规则(主题),可以将消息根据指定的 routing key 路由到匹配到的队列中,也是在
生产中比较常见的一种消息处理方式。
问题:RabbitMQ 本身是基于异步的消息处理,是否可以同步实现?
解决:采用 RPC 模式。
Rabbit MQ消息的事物机制
RabbitMQ 为我们提供了两种方式:
-
通过 AMQP 事务机制实现,这也是 AMQP 协议层面提供的解决方案;
-
通过将 channel 设置成 confirm 模式来实现;
RabbitMQ 中与事务机制有关的方法有三个:txSelect(), txCommit()以及
txRollback(), txSelect 用于将当前 channel 设置成 transaction 模式,txCommit()
用于提交事务,txRollback()用于回滚事务,在通过 txSelect()开启事务之后,我们便
可以发布消息给 broker 代理服务器了,如果 txCommit()提交成功了,则消息一定到达了
broker 了,如果在
txCommit()执行之前 broker 异常崩溃或者由于其他原因抛出异常,这
个时候我们便可以捕获异常通过 txRollback()回滚事务
事务确实能够解决 producer 与
broker 之间消息确认的问题,只有消息成功被 broker
接受,事务提交才能成功,否则我们便可以在捕获异常进行事务回滚操作同时进行消息重发,
但是使用事务机制的话会降低 RabbitMQ 的性能,那么有没有更好的方法既能保障
producer 知道消息已经正确送到,又能基本上不带来性能上的损失呢?从 AMQP 协议的层
面看是没有更好的方法,但是 RabbitMQ 提供了一个更好的方案,即将 channel 信道设置
成 confirm 模式。
Confirm
确认模式原理
生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布
的消息都会被指派一个唯一的 ID(从
1 开始),一旦消息被投递到所有匹配的队列之后,
broker 就会发送一个确认给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经
正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会将消息写入磁盘之后
发出,broker 回传给生产者的确认消息中 deliver-tag 域包含了确认消息的序列号,此外
broker 也可以设置
basic.ack 的 multiple 域,表示到这个序列号之前的所有消息都已经
得到了处理。
confirm 模式最大的好处在于他是异步的,一旦发布一条消息,生产者应用程序就可以
在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可
以通过回调方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消息,生产者应用程序同样可以在回调方法中处理该 nack 消息。
Confirm
确认机制代码实现
实现生产者 confirm 机制有三种方式:
- 普通 confirm 模式:每发送一条消息后,调用 waitForConfirms()方法,等待服
务器端 confirm。实际上是一种串行 confirm 了。
- 批量 confirm 模式:每发送一批消息后,调用 waitForConfirmsOrDie()方法,
等待服务器端 confirm。
- 异步 confirm 模式:提供一个回调方法,服务端 confirm 了一条或者多条消息后
Client 端会回调这个方法。