业务上有时会有遇上需要延迟队列的场景
如:
订单30分钟未支付,自动取消。
某项任务指派给某个人,指派失败后30分钟后重试。
RabbitMQ实现延迟队列
RabbitMQ原生是不支持延迟队列的,不过可以通过消息的TTL和死信Exchange来实现延迟队列。
所以要想实现延迟队列,需要先理解消息的TTL及死信Exchange
消息的TTL(Time To Live)
消息TTL就是消息的过期时间。
RabbitMQ可以通过设置队列过期时间或消息设置过期时间,来实现对消息设置过期时间。
对队列设置过期时间,即对队列增加"x-message-ttl"属性,若设置了此属性,队列内的消息过期时间据为队列的过期时间。
另一种方式是,对每一条消息都设置过期时间。
若两种TTL均存在,那么会取小的作为过期时间。
注:这两种设置过期时间还是有区别的,如果设置队列的TTL,则消息过期就会被丢弃。而单独为每个消息设置过期时间,消息即使过期也未必会被丢弃,因为消息的过期检查是在投递给消费者前检查的。若当前队列存在消息积压,则已过期的消息可能会存在较长时间。另外,若不设置消息TTL则消息永不过期,若将消息的TTL设置成0,则表示除非可以直接将消息投递给消费者,否则消息将被丢弃。
死信Exchage DLX(Dead Letter Exchange)
死信交换机,消息在满足以下条件时会进入死信交换机。
- 消息被Consumer拒收了,且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。
- 消息的TTL到了,消息过期了。
- 队列的长度限制满了,排在前面的消息会被丢弃进入死信队列。
DLX其实就是一种普通的exchange,和创建的其他exchange没有区别。只是在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去。
原理图
延迟任务通过消息的TTL和Dead Letter Exchange来实现。我们需要建立2个队列,一个用于发送消息,一个用于消息过期后的转发目标队列。
生产者生产一条延时消息,根据需要延时时间的不同,利用不同的routingkey将消息路由到不同的延时队列,每个队列都设置了不同的TTL属性,并绑定在同一个死信交换机中,消息过期后,根据routingkey的不同,又会被路由到不同的死信队列中,消费者只需要监听对应的死信队列进行处理即可