基于Redis实现延时任务

在 Redis 中实现延时任务的功能主要有两种方案:Redis 过期事件监听Redisson 内置的延时队列。下面将详细解释这两种方案的原理、优缺点以及面试时需要注意的相关细节。


方案 1:Redis 过期事件监听

实现原理

Redis 从 2.0 版本开始支持**发布/订阅(pub/sub)**功能,这种机制类似于消息队列。Redis 中引入了 channel(频道)的概念,发布者可以向某个频道发布消息,订阅者可以订阅相应的频道以接收消息。

为了实现延时任务功能,Redis 提供了 __keyevent@<db>__:expired 频道,这是一个内置频道,用于监听 key 的过期事件。工作流程如下:

  1. 设置 Key 过期时间:使用 SET key value EX seconds 设置一个带有过期时间的键。
  2. 等待过期事件:当 key 到期并被删除时,Redis 会向 __keyevent@<db>__:expired 频道发布一个过期事件。
  3. 监听频道:延时任务系统可以通过订阅该频道获取过期事件,从而执行对应的延时任务逻辑。
    让我更详细地解释 Redis 的 pub/sub 功能和 __keyevent@<db>__:expired 频道在延时任务中的使用原理。

1. 什么是 Redis 的 pub/sub 机制?

Redis 的 pub/sub(发布/订阅)机制是一种消息通信模式,用于在发布者(Publisher)和订阅者(Subscriber)之间传递消息。具体来说:

  • 发布者:向指定的 channel(频道)发布消息。Redis 负责将消息发送给订阅该 channel 的所有订阅者。
  • 订阅者:通过订阅一个或多个 channel,来接收发布者发送的消息。

pub/sub 的典型特点是实时性

  • 如果在发布消息的瞬间有订阅者在线,那么订阅者会立即接收到消息。
  • 如果发布消息时没有订阅者在线,那么消息直接丢弃,不会存储或等待未来的订阅者。

2. __keyevent@<db>__:expired 频道是什么?

Redis 提供了一些默认的 channel,用来通知特定事件的发生。__keyevent@<db>__:expired 是其中一个默认的 channel,专门用于通知 key 的过期事件

  • key 到期事件:当某个设置了过期时间的 key 被 Redis 删除时(通过惰性删除或定期删除),Redis 会向 __keyevent@<db>__:expired 频道发布一条消息,通知这个 key 已过期。
  • 消息内容:过期消息的内容是过期的 key 名称,订阅者可以根据收到的 key 名称来进行进一步处理,比如执行延时任务。

注意:这个 channel 的作用只是用于通知 key 的过期事件,它不会真正存储 key,也不会记录过期 key 的任何数据。

3. 延时任务是如何使用 __keyevent@<db>__:expired 的?

在需要实现延时任务的场景中,可以利用 Redis 的 __keyevent@<db>__:expired 频道来检测某个 key 是否过期,从而触发相关任务。工作流程如下:

  1. 设置带有过期时间的 key:通过 SET key value EX seconds 命令,将任务信息存储在 Redis 中,同时设置一个过期时间。

    • 比如:SET my_task_key "task_data" EX 60,表示在 60 秒后该 key 过期。
  2. 监听 __keyevent@<db>__:expired 频道:在应用中,启动一个订阅者订阅 __keyevent@<db>__:expired 频道。

    • 一旦该频道发布过期消息,订阅者就会收到这个过期事件的通知,并获得过期 key 的名称(例如 my_task_key)。
  3. 处理过期事件,执行延时任务:收到过期事件后,订阅者可以根据过期 key 的名称,从 Redis 或其他存储中获取任务详情,并执行相应的延时任务。

示例

假设我们想要实现一个任务,在 60 秒后执行。可以按以下步骤进行:

  1. 设置任务

    SET my_task_key "task_data" EX 60
    

    这样 my_task_key 会在 60 秒后自动过期。

  2. 监听过期事件
    在应用中启动一个进程,订阅 __keyevent@<db>__:expired 频道。

    SUBSCRIBE __keyevent@0__:expired
    
  3. 收到过期通知,执行任务
    my_task_key 过期并被 Redis 删除时,__keyevent@0__:expired 频道会发布消息 my_task_key。订阅者收到这个消息后,就可以根据 my_task_key 执行相关任务,比如发送通知、处理数据等。

存在的问题
  1. 时效性差:Redis 过期事件是在服务器实际删除 key 时发布的,而不是在 key 过期的瞬间发布。Redis 使用的是“惰性删除”和“定期删除”策略:

    • 惰性删除:只有当访问 key 时,才会检查并删除已过期的 key。
    • 定期删除:Redis 每隔一段时间随机抽取一批 key 进行过期检查,删除过期的 key,但会控制删除操作的时长和频率,以避免占用过多的 CPU 资源。

    由于这两种删除策略的存在,可能出现 key 实际过期后没有被立即删除的情况,导致延时任务不能准确执行。

  2. 丢失消息:Redis 的 pub/sub 模式不支持持久化。如果在消息发布时没有任何订阅者,消息将被直接丢弃,无法保证消息不丢失。这种缺陷在高并发、多节点部署下尤其明显。如果发布过期事件时没有任何订阅者在监听 __keyevent@<db>__:expired,则该消息会被直接丢弃,订阅者无法事后获取到这条消息。这就是 Redis pub/sub 机制的特性,无法存储和回放消息。

  3. 多实例下消息重复消费:Redis pub/sub 是广播模式,所有订阅者都能接收到消息。当有多个实例订阅同一个频道时,多个实例会收到相同的过期事件,可能导致延时任务的重复消费。多实例架构中,这意味着:如果你有多个应用实例(例如多个微服务或多个分布式进程)都在订阅 keyevent@:expired 频道,当一个 key 过期时,所有订阅者实例都会收到过期事件的通知。每个实例都会独立地处理该过期事件,导致相同的延时任务被执行多次。


方案 2:Redisson 内置的延时队列

实现原理

Redisson 是一个 Redis 的 Java 客户端,提供了很多开箱即用的功能,其中就包括延时队列 RDelayedQueue。Redisson 使用 Redis 的有序集合(SortedSet)来实现延时任务:

  1. 添加任务到延时队列:将延时任务插入到 SortedSet 中,任务的过期时间被设置为分数(score),用于确定任务的触发时间。
  2. 定期扫描过期任务:Redisson 使用 zrangebyscore 命令来查找 SortedSet 中已过期的任务。
  3. 转移到就绪队列:将过期任务从 SortedSet 中移除,并添加到一个阻塞队列(就绪队列)中。
  4. 消费任务:消费端监听就绪队列,任务一旦到达就绪队列即可执行。
优势
  1. 减少丢失消息的可能DelayedQueue 中的消息可以持久化,即使 Redis 意外宕机,根据持久化策略可以在重启后恢复队列中的任务,只可能丢失少量消息,可以通过补偿机制(如定期扫描数据库)来进一步保证消息可靠性。

  2. 无重复消费问题:每个消费端从同一个阻塞队列中获取任务,不存在广播模式下的重复消费问题。Redisson 延时队列采用有序集合 + 阻塞队列组合,可以确保任务只会被消费一次。

  3. 高效率:相比于轮询整个队列,Redisson 延时队列只会扫描符合条件的任务,避免了不必要的操作,提升了性能。

面试优势

当被问到选择 Redisson 延时队列的原因时,可以从以下几个方面解释:

  • 高可靠性:消息不会丢失,保证了任务的准确执行。
  • 去重处理:避免了多实例下的消息重复消费问题。
  • 效率更高:利用 Redis SortedSet 和阻塞队列,降低了系统的性能开销。

可能的面试问题

在选择了 Redisson 延时队列方案后,面试官可能会深入问一些相关问题,比如:

  1. Redis 延时任务的实现原理是什么?为什么使用 SortedSet?

    • 可以回答:Redisson 延时队列使用 Redis 的 SortedSet(有序集合)来存储任务,通过设置每个任务的过期时间作为分数(score),然后使用 zrangebyscore 查找并转移到堵塞就绪队列,确保任务按时间顺序执行。
  2. 如何保证消息不会丢失?

    • 可以回答:Redisson 延时队列的任务会被持久化到 Redis,即使 Redis 宕机,持久化策略会保证数据的恢复。还可以考虑增加补偿机制,如定期扫描数据库,防止极端情况的消息丢失。
  3. 多实例如何避免重复消费?

    • 可以回答:Redisson 延时队列使用阻塞队列来转移已到期的任务,消费端从同一个阻塞队列获取任务,因此不会产生重复消费的问题。
  4. 在实际项目中什么时候选用 Redis 延时队列?什么时候选用消息队列?

    • 可以回答:Redis 延时队列适用于延时精度要求较高、流量较小的延时任务场景;而消息队列(如 RabbitMQ、Kafka)适合处理高吞吐量、大规模的延时任务需求,具备更高的可靠性和分布式能力。

总结

  • Redis 过期事件监听:简单易用,但由于惰性删除和定期删除策略,可能存在时效性差、消息丢失和重复消费问题。
  • Redisson 延时队列:基于 Redis 的 SortedSet 和阻塞队列实现,可靠性高,避免了重复消费问题,更适合生产环境中的延时任务需求。

在实际项目中,优先考虑使用 Redisson 的延时队列或其他专业的消息队列方案,这样可以在可靠性、可维护性和性能上获得更好的表现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/19532.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

TypeScript泛型基础知识

1.1 泛型 泛型是可以在保证类型安全的前提下&#xff0c;让函数等与多种类型一起工作&#xff0c;从而实现复用&#xff0c;常用于&#xff1a;函数、接口、class中。 需求&#xff1a;创建一个id函数&#xff0c;传入什么数据就返回该数据本身&#xff08;也就是说&#xff0c…

多线程的安全问题

什么是线程安全问题&#xff1f; 多个线程&#xff0c;访问同一资源&#xff0c;出现了问题&#xff0c;就是线程安全问题&#xff08;数据不准确&#xff0c;或者直接报错&#xff09; 1&#xff09;错误演示&#xff1a; public class Demo04 {static int tickedNum 100;/…

推荐一套相片复原工具:Focus Magic

Focus Magic是一套相片复原工具&#xff0c;能够帮助你修补及强化那些模糊不清楚的影像。其他那些锐利化工具只能够修补那些只有一点模糊的相片&#xff0c;但有了Focus Magic你就可以把那些根本完全没对准焦距的相片重新对准焦距。程序还可以以插件的形式作为其他图形处理工具…

C++:类的继承

在C中&#xff0c;类的继承是一种面向对象编程&#xff08;OOP&#xff09;的重要特性&#xff0c;它允许一个类&#xff08;子类或派生类&#xff09;继承另一个类&#xff08;基类或父类&#xff09;的属性和方法。通过继承&#xff0c;可以重用现有的代码&#xff0c;减少重…

详细解读Gson 、Jackson 、FastJson 三大json序列化工具

一 gson Google提供的用来java对象和JSON数据之间进行映射的JAVA类库 优缺点 优点&#xff1a;快速、高效&#xff0c; 依赖少只有一个jar包&#xff0c;面向对象&#xff0c;数据传输解析方便 缺点&#xff1a;速度较慢 mvn依赖 <dependency><groupId>com.go…

版块控制---git

引入 设想&#xff0c;当我们写论文时&#xff0c;对第一版不够满意时&#xff0c;想做出修改但是又怕修改时回毁掉整个论文版本&#xff0c;所以我们通常会进行备份&#xff0c;以防止数据被修改后的崩毁&#xff0c;版块控制就是这个合理创建管理备份的过程&#xff0c;而且这…

BLE 蓝牙客户端和服务器连接

蓝牙通信在设计小型智能设备时非常普遍&#xff0c;之前一直没有使用过&#xff0c;最近使用ardunio ESP32 做了一些实验&#xff0c;做了一个收听播客的智能旋钮&#xff08;Smart Knob&#xff09;&#xff0c;它带有一个旋转编码器和两个按键。 本文介绍BLE 服务器Server和W…

图(Graph)的概念和遍历

目录 定义 相关概念 无向图&#xff08;Undirected graphs) 有向图&#xff08;Directed graphs&#xff09; 完全图 稀疏图 稠密图 权&#xff08;Weight&#xff09; 网&#xff08;Network&#xff09; 子图&#xff08;Subgraph&#xff09; 图的顶点与边间关系 …

python成长技能之正则表达式

文章目录 一、认识正则表达式二、使用正则表达式匹配单一字符三、正则表达式之重复出现数量匹配四、使用正则表达式匹配字符集五、正则表达式之边界匹配六、正则表达式之组七、正则表达式之贪婪与非贪婪 一、认识正则表达式 什么是正则表达式 正则表达式&#xff08;英语&…

Unity图形学之RenderQueue

1.指定物体的渲染顺序 Tags { “Queue” “XXXX” } 取值类型&#xff1a; Background&#xff1a; 对应数值为 1000&#xff0c;用于需要被最先渲染的对象&#xff0c;。 Geometry&#xff1a; 对应数值为 2000, 用于不透明的物体。这个是默认的选项&#xff08;如果不指明…

i春秋-破译(凯撒密码+数字替换单词中的字母)

练习平台地址 竞赛中心 题目描述 题目内容 就是破译&#xff01;&#xff01;&#xff01; 解题 观察到最后一段是四个字母加上{xxxxx}的形式&#xff0c;很像flag&#xff0c;我们猜测要破译的主要是这个片段 大括号依然存在&#xff0c;那么可能是通过凯撒密码来加密的&a…

丹摩征文活动|平台评测与使用体验报告

一、基本信息 目标产品 丹摩智算平台www.damodel.com 体验设备 台式机 体验系统/环境 Windows 10/浏览器 体验时间 2024/11 二、产品信息 产品类型&#xff1a;云计算服务提供商 产品定位&#xff1a;提供AI开发和算力GPU租赁服务的平台。它旨在简化AI开发流程&#…

Stable Diffusion核心网络结构——CLIP Text Encoder

&#x1f33a;系列文章推荐&#x1f33a; 扩散模型系列文章正在持续的更新&#xff0c;更新节奏如下&#xff0c;先更新SD模型讲解&#xff0c;再更新相关的微调方法文章&#xff0c;敬请期待&#xff01;&#xff01;&#xff01;&#xff08;本文及其之前的文章均已更新&…

20241118给荣品PRO-RK3566开发板刷Rockchip原厂的buildroot后使用iperf3打流

20241118给荣品PRO-RK3566开发板刷Rockchip原厂的buildroot后使用iperf3打流 2024/11/18 16:38 缘起&#xff0c;使用荣品的DTS。 Y:\RK3566_RK3568_Linux5.10_V1.2.0\device\rockchip\.chips\rk3566_rk3568\rockchip_rk3566_evb2_lp4x_v10_defconfig 1、指定RK_KERNEL_DTS_NAM…

基于java+SpringBoot+Vue的基于web的智慧社区设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…

分析一个库 便于以后的使用 WiFiManager.h 2024/11/18

这一段是开启服务器 的 这些都不是重点 重点是那个R_update ,没猜错的话应该是升级的功能 直接索引到定义看看 ,很明显这里是设置了一个 web 访问地址 那就只有换个思路往后查找 handleUpdate 找到这个函数定义 void WiFiManager::handleUpdate() {#ifdef WM_DEBUG_LEVELDEBUG…

学习笔记024——Ubuntu 安装 Redis遇到相关问题

目录 1、更新APT存储库缓存&#xff1a; 2、apt安装Redis&#xff1a; 3、如何查看检查 Redis版本&#xff1a; 4、配置文件相关设置&#xff1a; 5、重启服务&#xff0c;配置生效&#xff1a; 6、查看服务状态&#xff1a; 1、更新APT存储库缓存&#xff1a; sudo apt…

【MySQL系列】深入理解MySQL中的存储、排序字符集

前言 在创建数据库时&#xff0c;我们经常会需要填写数据库的所用字符集、排序规则&#xff0c;字符集和排序规则是两个非常重要的概念&#xff0c;它们决定了数据库如何存储和比较字符串数据。在 MySQL 中&#xff0c;常用的存储字符集有 utf8、utf8mb4&#xff0c;而排序字符…

tcp 超时计时器

在 TCP&#xff08;传输控制协议&#xff09;中有以下四种重要的计时器&#xff1a; 重传计时器&#xff08;Retransmission Timer&#xff09; 作用&#xff1a;用于处理数据包丢失的情况。当发送方发送一个数据段后&#xff0c;就会启动重传计时器。如果在计时器超时之前没有…