主要记录一下开发时用到 rabbitmq 死信队列的一些笔记
延迟队列的应用场景
1、未支付订单定时取消
2、定时清理缓存对象、空闲连接等
3、下单成功后 30 分钟内,按不同时间间隔发送通知等(1min、3min、10min 发一次)
1、设置队列的过期时间
$this->channel->queue_declare($this->retry_queue(),
false,
true,
false,
false,
false,
new AMQPTable(
[
# 不设置 x -dead-letter-routing-key,使用原先的 routing_key,10s 过期后自动重回原先的队列里面,那 x -dead-letter-exchange 交换机就需绑定原先队列
'x-dead-letter-exchange' => $this->retry_exchange(),
# 10s
'x-message-ttl' => 10000,
]
)
);
推送到该队列的所有消息(不设 ttl),10s 之后都会过期,根据原来的 routing_key,进入到指定的 exchange,进而进到指定队列。
2、设置消息的过期时间
$message = new AMQPMessage(
'msg',
array(
# 消息持久化
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSITENT,
# ttl 过期时间
'expiration' => 50000,
)
);
每个消息都设置相同的过期时间,到期后消息就会失效。
3、同时设置队列、消息的过期时间
如果同时设置,则消息的过期时间会 取决于较小的值,比如队列的‘x-message-ttl’设置为 10s,消息的‘expiration’设置为 50s,则 10s 之后这个消息就会失效。
4、后续
单单设置队列的 ttl,或者单单设置相同的消息过期时间,死信队列是能正常工作的。但是设置不同的消息过期时间,就可能无法正常使用死信队列了。
队列不设 ttl
$this->channel->queue_declare($this->retry_queue(),
false,
true,
false,
false,
false,
new AMQPTable(
[
# 不设置 x -dead-letter-routing-key,使用原先的 routing_key,10s 过期后自动重回原先的队列里面,那 x -dead-letter-exchange 交换机就需绑定原先队列
'x-dead-letter-exchange' => $this->retry_exchange(),]
)
);
第一个消息设置 500s 过期,先推进队列
$message = new AMQPMessage(
'msg',
array(
# 消息持久化
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSITENT,
# ttl 过期时间
'expiration' => 500000,
)
);
第二个消息设置 5s 过期,后推进队列
$message = new AMQPMessage(
'msg',
array(
# 消息持久化
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSITENT,
# ttl 过期时间
'expiration' => 5000,
)
);
结果发现,5s 之后,队列里还存在 2 个消息。说明第二个消息并没有“真的过期失效”。原因是位于队列首部的消息没有过期。而 rabbitmq 的死信队列,是 基于首部消息实现 的。
5、结论
当 MQ 检查队列中的第一个消息时,发现其并未过期,则不会继续检查之后的消息了。即使之后的消息过期了,也会因为没在队列头部而无法流转到其他队列,这是 MQ 队列的特性决定的。你不能去消费队列中间的消息,队列必须先进先出。
对于设置队列 TTL 属性的方法,一旦消息过期,就会从队列中抹去,而设置消息头部属性,即使消息过期,也不会马上从队列中抹去,因为每条消息是否过期时在即将投递到消费者之前判定的,为什么两者得处理方法不一致?因为第一种方法里,队列中已过期的消息肯定在队列头部,RabbitMQ 只要定期从队头开始扫描是否有过期消息即可,而第二种方法里,每条消息的过期时间不同,如果要删除所有过期消息,势必要扫描整个队列,所以不如等到此消息即将被消费时再判定是否过期,如果过期,再进行删除。
官方的叙述
"Only when expired messages reach the head of a queue will they actually be discarded (or dead-lettered)." 只有当过期的消息到了队列的顶端(队首),才会被真正的丢弃或者进入死信队列。
引用参考
http://blog.lbanyan.com/rabbitmq_delay/
https://blog.csdn.net/u013256816/article/details/54916011
https://juejin.im/post/5b5e52ecf265da0f716c3203