rabbitmq延迟队列(死信队列)
主要记录一下开发时用到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