记住,永远不要使用 Redis 过期监听实现定时任务!
阅读本文大概需要 3.5 分钟。
来自:cnblogs.com/Finley/p/16395466.html
使用 rocketmq、rabbitmq、pulsar 等消息队列的延时投递功能
使用 redisson 提供的 DelayedQueue
使用 redis 的过期监听
使用 rabbitmq 的死信队列
使用非持久化的时间轮
redis 过期监听
keyspace-notifications
: timing-of-expired-events
中明确指出:Basically expired events are generated when the Redis server deletes the key and not when the time to live theoretically reaches the value of zero
fire and forget
)策略,并不像消息队列一样保证送达。当订阅事件的客户端会丢失所有在断线期间所有分发给它的事件。rabbitmq 死信
Dead Letter
) 是 rabbitmq
提供的一种机制。当一条消息满足下列条件之一那么它会成为死信:消息被否定确认(如
channel.basicNack
) 并且此时requeue 属性被设置为false。消息在队列的存活时间超过设置的TTL时间
消息队列的消息数量已经超过最大队列长度
创建一个交换机作为死信交换机
在业务队列中配置
x-dead-letter-exchange
和x-dead-letter-routing-key
,将第一步的交换机设为业务队列的死信交换机在死信交换机上创建队列,并监听此队列
rabbitmq-delayed-message-exchange
,推荐使用官方插件来做延时消息。这里说点题外话,使用 redis 过期监听或者 rabbitmq 死信队列做延时任务都是以设计者预想之外的方式使用中间件,这种出其不意必自毙的行为通常会存在某些隐患,比如缺乏一致性和可靠性保证,吞吐量较低、资源泄漏等。比较出名的一个事例是很多人使用 redis 的 list 作为消息队列,以致于最后作者看不下去写了 disque 并最后演变为 redis stream
。工作中还是尽量不要滥用中间件,用专业的组件做专业的事
时间轮
redisson delayqueue
redisson delayqueue
是一种基于 redis zset
结构的延时队列实现。delayqueue
中有一个名为 timeoutSetName
的有序集合,其中元素的 score
为投递时间戳。delayqueue
会定时使用 zrangebyscore
扫描已到投递时间的消息,然后把它们移动到就绪消息列表中。delayqueue
保证 redis 不崩溃的情况下不会丢失消息,在没有更好的解决方案时不妨一试。redisson delayqueue
等定时任务中间件时可以同时使用扫描数据库的方法作为补偿机制,避免中间件故障造成任务丢失。结论
首先推荐使用
rocketmq
、pulsar
等拥有定时投递功能的消息队列。在不方便获得专业消息队列时可以考虑使用
redisson delayqueue
等基于 redis 的延时队列方案,但要为 redis 崩溃等情况设计补偿保护机制。在无法使用
redisson delayqueue
等方案时可以考虑使用时间轮。由于时间轮重启远比 redis 重启要频繁,定时扫库等保护机制更为重要。永远不要使用 redis 过期监听实现定时任务。
互联网初中高级大厂面试题(9个G) 内容包含Java基础、JavaWeb、MySQL性能优化、JVM、锁、百万并发、消息队列、高性能缓存、反射、Spring全家桶原理、微服务、Zookeeper......等技术栈!
⬇戳阅读原文领取! 朕已阅
评论