Springboot使用Redis实现定时任务的三种方式

 更新时间:2025年06月25日 10:10:23   作者:weixin_43833540  
本文介绍了三种Redis定时任务实现方式,键空间通知,有序集合轮询,分布式锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、基于 Redis 键空间通知(适合精确延时任务)

原理:利用 Redis 的键过期事件(EXPIRE)触发任务执行,通过监听 __keyevent@*__:expired 通道捕获事件。
步骤

启用 Redis 键空间通知(redis.conf 或运行时配置):

CONFIG SET notify-keyspace-events Ex

Spring Boot 监听器实现

@Component
public class KeyExpiredListener extends KeyExpirationEventMessageListener {
    public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiredKey = new String(message.getBody());
        if (expiredKey.startsWith("task:")) { // 过滤业务键
            System.out.println("执行任务: " + expiredKey);
            // 例如:task:123 过期时执行订单超时逻辑
        }
    }
}

注册监听器

@Configuration
public class RedisConfig {
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory factory, 
                                           KeyExpiredListener listener) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        container.addMessageListener(listener, new PatternTopic("__keyevent@*__:expired"));
        return container;
    }
}

调度任务(设置带过期时间的键):

@Service
public class TaskScheduler {
    @Autowired
    private StringRedisTemplate redisTemplate;

    public void scheduleTask(String taskId, long delaySeconds) {
        redisTemplate.opsForValue().set("task:" + taskId, "data", 
                delaySeconds, TimeUnit.SECONDS); // 键在 delaySeconds 秒后过期
    }
}

注意:需在 Redis 配置中开启 notify-keyspace-events Ex

二、基于 Redis 有序集合轮询(适合批量定时任务)

原理:将任务执行时间作为 ZSETscore,通过定时任务查询到期的任务并执行。
步骤

添加任务到 ZSET

public void addTask(String taskId, Instant executeTime) {
    stringRedisTemplate.opsForZSet().add("scheduled_tasks", taskId, 
          executeTime.getEpochSecond());
}

定时扫描并执行任务(每分钟轮询):

@Scheduled(cron = "0 * * * * *") // 每分钟执行
public void pollTasks() {
    long now = Instant.now().getEpochSecond();
    Set<String> tasks = stringRedisTemplate.opsForZSet()
          .rangeByScore("scheduled_tasks", 0, now); // 获取所有到期任务

    for (String task : tasks) {
        System.out.println("执行任务: " + task);
        // 执行后移除任务
        stringRedisTemplate.opsForZSet().remove("scheduled_tasks", task);
    }
}

优点:避免键空间通知的丢失风险,适合任务量大的场景。

三、基于 Redis 分布式锁(防集群任务重复执行)

原理:在分布式环境中,通过 Redis 锁确保同一时间只有一个实例执行定时任务。
代码示例

@Component
public class DistributedTask {
    @Autowired
    private StringRedisTemplate redisTemplate;
    private static final String LOCK_KEY = "TASK_LOCK:ORDER_CLEAN";

    @Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点执行
    public void dailyTask() {
        Boolean locked = redisTemplate.opsForValue()
                .setIfAbsent(LOCK_KEY, "locked", Duration.ofMinutes(10));
        if (Boolean.TRUE.equals(locked)) {
            try {
                cleanExpiredOrders(); // 执行核心任务
            } finally {
                redisTemplate.delete(LOCK_KEY); // 释放锁(可选)
            }
        }
    }
}

关键点

  • 使用 setIfAbsent 原子操作获取锁,避免并发冲突。
  • 锁自动过期防止死锁(如任务执行超时)。

​​版本要求​​:
​​Spring Data Redis ≥ 2.3.0​​:该版本引入了 setIfAbsent(key, value, duration) 方法,支持​​原子性设置键值+过期时间​​(对应 Redis 的 SET key value NX EX seconds 命令)

四、方案对比

方案适用场景注意事项
键空间通知精确延时任务(如30分钟后关单)需配置 Redis,事件可能丢失
有序集合轮询批量任务、高可靠性场景需自行处理任务分页和重试
分布式锁集群环境防重复执行(如日报生成)锁超时时间需大于任务执行时间

*补充:

  • 关键业务(如支付超时)建议结合 数据库日志+重试机制 补偿;
  • 高频任务优先选 ZSET 轮询,避免键空间通知的性能瓶颈;
  • 分布式锁的锁键需包含业务标识(如 LOCK_KEY:业务名)。

到此这篇关于Springboot使用Redis实现定时任务的三种方式的文章就介绍到这了,更多相关Springboot Redis 定时任务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • springboot整合prometheus实现资源监控的详细步骤

    springboot整合prometheus实现资源监控的详细步骤

    Spring Boot与Prometheus的整合可以实现对Spring Boot应用的实时监控,有助于更好地维护应用的性能,本文给大家介绍springboot整合prometheus实现资源监控的详细步骤,感兴趣的朋友跟随小编一起看看吧
    2024-11-11
  • 浅谈java String.split丢失结尾空字符串的问题

    浅谈java String.split丢失结尾空字符串的问题

    下面小编就为大家带来一篇浅谈java String.split丢失结尾空字符串的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • springMVC几种页面跳转方式小结

    springMVC几种页面跳转方式小结

    本篇文章主要介绍了springMVC 几种页面跳转方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • SpringBoot开发案例之配置Druid数据库连接池的示例

    SpringBoot开发案例之配置Druid数据库连接池的示例

    本篇文章主要介绍了SpringBoot开发案例之配置Druid数据库连接池的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • SpringBoot处理跨域请求的四种方法

    SpringBoot处理跨域请求的四种方法

    在现代Web应用中,由于安全性和隐私的考虑,浏览器限制了从一个域向另一个域发起的跨域HTTP请求,解决这个问题的一种常见方式是实现跨域资源共享(CORS),SpringBoot提供了多种方式来处理跨域请求,本文将介绍其中的几种方法,感兴趣的朋友可以参考下
    2023-12-12
  • 深入理解java中的synchronized关键字

    深入理解java中的synchronized关键字

    这篇文章主要介绍了java中的synchronized关键字,有需要的朋友可以参考一下
    2013-12-12
  • 什么是jsoup及jsoup的使用

    什么是jsoup及jsoup的使用

    jsoup是一款基于Java的HTML解析器,它提供了一套非常省力的API,不但能直接解析某个URL地址、HTML文本内容,而且还能通过类似于DOM、CSS或者jQuery的方法来操作数据,所以 jsoup 也可以被当做爬虫工具使用,这篇文章主要介绍了什么是jsoup及jsoup的使用,需要的朋友可以参考下
    2023-10-10
  • Java使用WebView实现桌面程序的技术指南

    Java使用WebView实现桌面程序的技术指南

    在现代软件开发中,许多应用需要在桌面程序中嵌入 Web 页面,例如,你可能需要在 Java 桌面应用中嵌入一部分 Web 前端,或者加载一个 HTML5 界面以增强用户体验,所以本文给大家介绍了Java使用WebView实现桌面程序的技术指南,需要的朋友可以参考下
    2025-05-05
  • MyBatis-Plus实现优雅处理JSON字段映射

    MyBatis-Plus实现优雅处理JSON字段映射

    默认情况下,MyBatis-Plus 是不支持直接映射 JSON 类型的,这时候就需要借助其他的方法,下面小编就来和大家讲讲MyBatis-Plus如何优雅处理JSON字段映射吧
    2025-04-04
  • Jmeter对响应数据实现断言代码实例

    Jmeter对响应数据实现断言代码实例

    这篇文章主要介绍了Jmeter对响应数据实现断言代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09

最新评论