传统的RabbitMQ实现延时队列的方式一般是死信队列+TTL,但是由于RabbitMQ本身的不足,可能会产生时序问题,而使延时效果不够理想,RabbitMQ引入了一个Delayed Message 插件来优化此问题
目录
死信队列+TTL
时序问题:在不引入插件的情况下,改变消息的TTL,在队列中会按照进入顺序读取,无法及时延迟
例:
消息1先进入延时队列,消息2紧接着进入,
消息1:延时2s;消息2:延时3s,结果:1先被消费,2在1s后被消费(正常)
消息1:延时5s;消息2:延时3s,结果:1和2同时被消费(2会等1先出队在判断有无过期,失败)
改进方式
Delayed Message 插件
RabbitMQ有一个延迟消息插件 rabbitmq-delayed-message-exchange,目前维护在 RabbitMQ 插件社区,可以声明 x-delayed-message 类型的 Exchange,消息发送时指定消息头 x-delay 以毫秒为单位将消息进行延迟投递。
实现原理
通过 x-delayed-message 声明的交换机,它的消息在发布之后不会立即进入队列,而是先将消息保存至 Mnesia(一个分布式数据库管理系统)这个插件将会尝试确认消息是否过期,首先要确保消息的延迟范围是 Delay > 0, Delay =<ERL_MAX_T(在 Erlang 中可以被设置的范围为 (2^32)-1 毫秒),如果消息过期通过 x-delayed-type 类型标记的交换机投递至目标队列,整个消息的投递过程也就完成了。
其他选择
延时队列还有很多其它选择,比如利用 Java 的DelayQueue,利用 Redis 的 zset,利用 Quartz或者利用 Kafka 的时间轮,这些方式各有特点,看需要适用的场景