一、消息何去何从
mandatory和
immediate是
channel.
basicPublish
方法中的两个参数,它们都有
当消息传递过程中不可达目的地时将消息返回给生产者的功能。
RabbitMQ
提供的备份交换器
(A
lternate
Exchange
)可以将未能被交换器路由的消息(没有绑定队列或者没有匹配的绑定〉存
储起来,而不用返回给客户端。
mandatory参数:
当mandatory
参数设为
true
时,交换器无法根据自身的
类型和路由键找到一个符合条件
的队列,那么
RabbitMQ
会调用
Basic.Return
命令将消息返回给生产者
。当
mandatory
数设置为
false
时,出现上述情形,则消息直接被丢弃。
那么生产者如何获取到没有被正确路由到合适队列的消息呢?这时候可以通过调用
channel
addReturnListener
来添加
ReturnListener
监昕器实现。
上面代码中生产者没有成功地将消息路由到队列,此时
RabbitMQ
会通过
Basic.Return
返回“
mandatory
test
”这条消息,之后生产者客户端通过
ReturnListener
监昕到了这个事
件。
代码的最后输出应该是“Basic.Return 返回的结果是:
mandatory
test"
immediate 参数(RabbitMQ3.0版本去掉了这个参数,建议使用TTL和DLX的方法替代)
当
immediate
参数设为
true
时,如果交换器在将消息路由到队列时发现队列上并不存在
任何消费者,那么这条消息将不会存入队列中。当与路由键匹配的所有队列都没有消费者时,
该消息会通过
Basic
.Return
返回
至生产者。
概括来说,
ma
datory
参数告诉服务器至少将该消息路由到一个队列中,否则将消息返
回给生产者
immediate
参数告诉服务器,如果该消息关联的队列上有消费者,则立刻投递:
如果所有匹配的队列上都没有消费者,则直接将消息返还给生产者,不用将消息存入队列而等
待消费者了。
备份交换器
备份交换器,英文名称为
Alternate
Exchange
,简称AE,或者更直白地称之为“备胎交换器”。
生产者在发送消息的时候如果不设置
mandatory
参数,那么消息在未被路由的情况下将会丢失:
如果设置了
mandatory
参数,那么需要添加
ReturnListener
的编程逻辑,生产者的代码将
变得复杂。如果既不想复杂化生产者的编程逻辑,又不想消息丢失,那么可以使用备份交换器,
这样可以将未被路由的消息存储在
RabbitMQ
中,再在需要的时候去处理这些消息。
可以通过在声明交换器(调用
channel
exchangeDeclare
方法)的时候添加
alternate-exchange
参数来实现,也可以通过策略(
Policy
,详细参考
6.3
节〉的方式实现。
如果两者同时使用,则前者的优先级更高,会覆盖掉
Policy
的设置。
同样,如果采用
Policy
的方式来设置备份交换器,可以参考如下:
rabbitmqctl set_policy
AE
”
<
norma
l
Exchange
♀”
、{”
alternate-exchange
:”
myAE
”}’
备份交换器其实和普通的交换器没有太大的区别,为了方便使用,建议设置为
fano
ut
类型,
如若读者想设置为
direct
或者
topic
的类型也没有什么不妥。需要注意的是,消息被重新发送到
备份交换器时的路由键和从生产者发出的路由键是一样的。
考虑这样一种情况
,如
果备份交换器的类型是
direct
并且有一个与其绑定
的队列,假设绑
定的路由键是
keyl
当某条携带路由键为
key2 的消息被转发到这个备份交换器的时候,备份
交换器没有匹配到合适的队列,则消息丢失。如果消息携带的路由键为
keyl
,则可以存储到队
列中。
对于备份交换器,总结了以下几种特殊情况:
如果设置的备份交换器不存在,客户端和
RabbitMQ
服务端都不会有异常出现,此时消
息会丢失。
如果备份交换器没有绑定任何队列,客户端和
RabbitMQ
服务端都不会有异常出现,此
时消息会丢失。
如果备份交换器没有任何匹配的队列,客户端和
RabbitMQ
服务端都不会有异常出现,
此时消息会丢失。
如果备份交换器和
mandatory
参数一起使用,那么
mandatory
参数无效。
二、过期时间(TTL)
TTL, Time to
Live
的简称,即过期时间
RabbitMQ
可以对消息和队列设
TTL
目前有两种方法可以设置消息的
TTL
。第一种方法是通过队列属性设置,队列中所有消息
都有相同的过期时间。第二种方法是对消息本身进行单独设置,每条消息的
TTL
可以不同。如
果两种方法一起使用,则消息的
TTL
以两者之间较小的那个数值为准。消息在队列中的生存时间
一旦超过设置
TTL
值时,就会变成“死信”(
Dead
Message
),消费者将无
法再收到该消息
(这点不是绝对的
,可以
参考
4.3
节)
通过队列属性设置消息
TTL
的方法是在
channel.queueDeclare
方法中加入
x-message
-ttl
参数实现的,这个
参数的单位是毫秒。
如果不设置
TTL
,则表示此消息不会过期
如果将
TTL
设置为
表示除非此时可以直
接将消息投递到消费者,否则该消息会被立即丢弃,这个特性可以部分替代
RabbitMQ
3.
版本
之前的
immediate
参数,之所以部分代替,是因为
immediate
参数在投递失败时会用
Basic.
Return
将消息返回(这个功能可以用死信队列来实现,详细参考
4.3
节)。
针对每条消息设置
TTL
的方法是在
channel.basicPublish
方法中加入
expiration
的属性参数,单位为毫秒。
通过 http API 接口设置
对于第一种设置队列
TTL
属性的方法,一旦消息过期,就会从队列中抹去,而在第二种方
法中,即使消息过期,也不会马上从队列中抹去,因为每条消息是否过期是在即将投递到消费
者之前判定的。
为什么这两种方法处理的方式不一样?因为第一种方法里,队列中己过期的消息肯定在队
列头部,
bb
itM
只要定期从队头开始扫描是否有过期的消息即可。而第二种方法里,每条消
息的过期时间不同,如果要删除所有过期消息势必要扫描整个队列,所以不如等到此消息即将
被消费时再判定是否过期,如果过期再进行删除即可。
设置队列的TTL(自动删除)
通过
channel
queueDeclare
方法中的
x-expires
参数可以控制队列被自动删除前处
于未使用状态的时间。未使用的意思是队列上没有任何的消费者,队列也没有被重新声明,并
且在过期时间段内也未调用过
Basic
Get
命令。
设置队列里的
TTL
可以应用于类似
RPC
方式的回复队列,在
RPC
中,许多队列会被创建
出来,但是却是未被使用的。
Rabb
itMQ
会确保在过期时间到达后将队列删除,但是不保障删除的动作有多及时
。在
RabbitMQ
重启后,持久化的队列的过期时间会被重新计算。
用于表示过期时间的
x-expires
参数以毫秒为单位,井且服从和
x-message-ttl
的约束条件,不过不能设置为0
。比如该参数设置为
1000
,则表示该队列如果在1
秒钟之内未
使用则会被删除。
死信队列
消息编程死信一般是由于一下几种情况:
消息被拒绝,并且设置requeue参数为false
消息过期
队列达到最大长度
对于
RabbitMQ
来说, D
LX
是一个非常有用的特性 它可以处理异常情况下,消息不能够
被消费者正确消费(消费者调用了
Basic.Nack
或者
Basic.Reject
)而被置入死信队列中
的情况,后续分析程序可以通过消费这个死信队列中的内容来分析当时所遇到的异常情况,进
而可以改善和优化系统。
LX
配合
TTL
使用还可以实现延迟队列的功能。
延迟队列:
延迟队列存储的对象是对应的延迟消息,所谓“延迟消息”是指当消息被发送以后,并不
想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费
延迟队列的使用场景有很多,比如:
在订单系统中,
个用户下单之后通常有 30
分钟的时间进行支付,如果30
分钟之内
没有支付成功,那么这个订单将进行异常处理,这时就可以使用延迟队列来处理这些
订单了。
用户希望通过手机远程遥控家里的智能设备在指定的时间进行工作。这时候就可以将
用户指令发送到延迟队列,当指令设定的时间到了再将指令推送到智能设备。
优先级队列
优先级队列,顾名思义,具有高优先级的队列具有高的优先权,优先级高的消息具备优先
被消费的特权。
RPC实现
一
般在
RabbitMQ
中进行
RPC
是很简单。客户端发送请求消息,服务端回复响应的消息
为了接收响应的消息,我们需要在请求消息中发送一个回调队列(参考下面代码中的
replyTo
可以使用默认的队列。
可以为每个RPC请求创建一个单一的回调队列。
持久化
RabbitMQ 的持久化分为三个部分:交换器的持久化、队列的持久化和消息的持久化
设置了队列和消息的持久化,当
RabbitMQ
服务重启之后,消息依旧存在。单单只设置队
列持久化,重启之后消息会丢失;单单只设置消息的持久化,重启之后队列消失,继而消息也
丢失。单单设置消息持久化而不设置队列的持久化显得毫无意义。
注意要点:
可以将所有的消息都设直为持久化,但是这样会严重影响
RabbitMQ
的性能(随机)。写入
磁盘的速度比写入内存的速度慢得不只一点点。对于可靠性不是那么高的消息可以不采用持久
化处理以提高整体的吞吐量。在选择是否要将消息持久化时,需要在可靠性和吐吞量之间做一
个权衡。