当前位置: 代码网 > it编程>编程语言>Java > Springboot使用Redis实现定时任务的三种方式

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

2025年06月25日 Java 我要评论
一、基于 redis 键空间通知(适合精确延时任务)原理:利用 redis 的键过期事件(expire)触发任务执行,通过监听 __keyevent@*__:expired 通道捕获事件。步骤:启用

一、基于 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 定时任务内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网! 

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com