比较典型的消息模型包括基于TopicExchange的消息模型、基于DirectExchange的消息模型和基于FanoutExchange的消息模型,这些消息模型都有一个特点,那就是消息一旦进入队列,将立即被对应的消费者监听消费,然而在某些业务场景中,有些业务数据对应的消息进入队列后不希望立即被处理,而是要求该消息可以延迟一定的时间,再被消费者监听消费处理,这就是死信队列/延迟队列出现的原因。
死信队列(延迟队列)
是什么
在传统企业级应用系统中,实现消息、业务数据的延迟处理一般是通过开启定时器的方式,轮询扫描并获取数据库中满足条件的业务数据,然后比较业务时间和当前时间,如果当前时间大于业务时间,那么说明该数据已经超过了指定的时间而未被处理,此时需要执行相应的业务逻辑,比如失效该数据记录、发送通知消息给指定用户,定时器每隔一段时间不间断的去扫描数据库表,并不断的获取满足业务条件的数据,知道手动关闭
相对于传统的定时器轮询,死信队列具有占用系统资源少,人为干预很少,以及自动消费处理。
专有词汇介绍
死信队列同样也有信息、交换机、路由、队列等专有名词,但是增加了3个名词
- DLX,即Dead Letter Exchange,死信交换机,是交换机的一种类型
- DLK,Dead Letter Routing-Key,死信路由,主要是跟DLX组合构成死信队列
- TTL,Time To Live,指进入死信队列中的消息可以存活的时间,当TTL到了就意味着消息死了,从而进入下一个中转站
来源
- 消息被拒绝(basic.reject或basic.nack)并且requeue=false.
- 消息TTL过期
- 队列达到最大长度(队列满了,无法再添加数据到mq中)
普通的消息在发生上述情况时,就会发生死信,之后消息将被重新投递到另一个交换机,也就是DLX(死信交换机)
简单点说就是没有被死信队列消费的消息将换个地方重新被消费,从而实现消息的延迟功能。而这个地方就是死信交换机
死信队列和延迟队列
从上面我们已经可以看出了,延迟队列就是利用死信队列实现的,在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间(过期消息会变为死信)和死信队列来模拟出延时队列
扩展
还可以使用Redis zset实现延迟队列,这个方案比较常用,简单有效。利用 Redis 的 sorted set 结构,使用 timeStamp 作为 score,比如你的任务是要延迟5分钟,那么就在当前时间上加5分钟作为 score ,轮询任务每秒只轮询 score 大于当前时间的 key即可,如果任务支持有误差,那么当没有扫描到有效数据的时候可以休眠对应时间再继续轮询。
优点:实现简单,应用快
缺点:单个 zset 肯定支持不了太大的数据量,如果你有几百万的延迟任务需求,这个方案明显支撑不了。定时器轮询方案可能会有异常终止的情况需要自己处理,同时消息处理失败的回滚方案,您也要自己处理。所以,sorted set 的方案并不是一个成熟的方案,他只是一个快速可供落地的方案。