Redis模拟延时队列 实现日程提醒

使用Redis模拟延时队列

实际上通过MQ实现延时队列更加方便,只是在实际业务中种种原因导致最终选择使用redis作为该业务实现的中间件,顺便记录一下。

该业务是用于日程短信提醒,用户添加日程后,就会被放入redis队列中等待被执行发送短信提醒业务。

本文介绍如何使用Redis来实现一个简单的延时任务队列,通过这个示例,可以帮助你理解如何利用Redis的有序集合特性来管理和执行延时任务。

设计思路

Redis有序集合(Sorted Set)可以很好地用来实现延时队列的功能。通过将任务的执行时间作为分数(score)来存入有序集合中,并定期检查集合中小于等于当前时间的任务来触发执行。

代码实现

JedisCluster连接初始化

首先,我们需要初始化JedisCluster连接来与Redis集群进行交互。

java 复制代码
private static final String ZSET_KEY = "sms_delayed_tasks";
private JedisCluster jedisCluster;

public void RedisClusterScheduler() {
    Set<HostAndPort> nodes = new HashSet<>();
    //从配置文件中读取redis集群配置
    for (String node : AcpCore.getProp("spring.redis.cluster.nodes").split(",")) {
        String[] hostPort = node.split(":");
        nodes.add(new HostAndPort(hostPort[0], Integer.parseInt(hostPort[1])));
    }

    GenericObjectPoolConfig<Jedis> poolConfig = new GenericObjectPoolConfig<>();
    poolConfig.setMaxTotal(128);
    poolConfig.setMaxIdle(128);
    poolConfig.setMinIdle(16);

    jedisCluster = new JedisCluster(nodes, 2000, 2000, 5, AcpCore.getProp("spring.redis.password"), poolConfig);

    if (!isCalled) {
        isCalled = true;
        startTaskChecker();
    }
}

添加延时任务

我们可以通过指定任务和执行时间来添加延时任务。该方法将执行时间转换为时间戳,并将任务存储在Redis有序集合中。

java 复制代码
public void addDelayedTask(String task, String time) {
    long executeTime = convertToTimestamp(time);
    if (executeTime > System.currentTimeMillis() / 1000) {
        jedisCluster.zadd(ZSET_KEY, executeTime, task);
        log.info("添加任务: " + task + " 执行时间: " + executeTime);
    } else {
        log.info("任务时间必须在当前时间之后: " + task);
    }
}

private long convertToTimestamp(String time) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    try {
        return sdf.parse(time).getTime() / 1000;
    } catch (ParseException e) {
        e.printStackTrace();
        return System.currentTimeMillis() / 1000;
    }
}

检查和执行任务

通过一个定时任务不断检查当前时间之前的任务并执行。

java 复制代码
private void startTaskChecker() {
    executorService.submit(() -> {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                checkAndExecuteTasks();
                Thread.sleep(1000);
            } catch (Exception e) {
                log.info(new Date() + "发生异常但不中断,异常是:" + e);
            }
        }
    });
}

private void checkAndExecuteTasks() {
    long currentTime = System.currentTimeMillis() / 1000;
    Set<String> tasks = jedisCluster.zrangeByScore(ZSET_KEY, 0, currentTime);
    
    for (String task : tasks) {
        jedisCluster.zrem(ZSET_KEY, task);
        executeTask(task);
    }
}

执行任务的逻辑

假设任务内容是一个JSON对象,执行逻辑在这里可以是任何操作,比如调用外部服务、发送消息等。

java 复制代码
private void executeTask(String taskJson) {
    JSONObject task = JSONObject.parseObject(taskJson);
    // 在此处添加具体的业务逻辑
    log.info("执行任务: " + task);
}

总结

通过Redis的有序集合和简单的定时器,能够实现一个简洁有效的延时任务队列。

当然,这个示例是一个简化的模型,在生产环境中,你需要考虑任务的幂等性、系统崩溃后的恢复策略、任务的优先级等问题。希望本文能为你提供实现延时队列的思路和参考。

复制代码
相关推荐
liuchangng2 分钟前
Jev 模型研究:从生成式大模型到决策式模型——System One、RLCD 校准与采用边界
java·javascript·人工智能·python·深度学习
GPU实战笔记3 分钟前
本地跑不动 llama.cpp,临时租云 GPU 怎么搭?从启动服务到环境复用
java·服务器·网络·人工智能·深度学习·llama
撸串-研究生8 分钟前
SSM + Vue 线上作业批改系统:在线答题、文件提交与教师批改闭环90608
java·ssm·在线答题·作业批改·文件提交
一 乐14 分钟前
养老院管理系统|基于springboot + vue养老院管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
wang_shu_mo_ran16 分钟前
Spring MVC 常用注解和用法(二)——url,cookie,session
java·mvc
杨运交17 分钟前
[074][示例]基于Redisson的分布式锁在定时任务中的实践与异常模拟
java·后端·spring
小刘在重生~18 分钟前
Java Lock 显式锁案例|ReentrantLock 解决多线程安全问题(超详细实战)
java·笔记·面试
步行cgn24 分钟前
Spring 中完整的 Scope 选项详解
java·后端·spring
hai_android44 分钟前
Android MeasureSpec 详解
android·java·kotlin
阳光宅男@李光熠2 小时前
【电子通识】一起学习TDK的EMC基础——电池兼容设计方法概述
java·前端·数据库