Redis实现定时任务

2023-11-20

Redis定时任务的核心在于"@Schedule"注解,Redis Zset,List数据结构,Redis管道技术

就从定时任务的执行流程开始写起

1.前端用户发起定时任务创建定时任务任务,像定时任务模块发起定时任务请求并且携带必要参数

首先我们在定时任务服务中中判断当前任务执行时间是否小于当前时间,

public long addTask(Taskinfo taskinfo) {
    if (null == taskinfo.getExecuteTime()){
        //保存消息到数据库中
        Calendar executeTime = Calendar.getInstance();
        executeTime.add(Calendar.MINUTE, 5);
        Date futureTime = executeTime.getTime();
        taskinfo.setExecuteTime(futureTime);
    }
    taskInfoMapper.insert(taskinfo);
    //判断当前时间与执行时间的关系
    if (new Date().compareTo(taskinfo.getExecuteTime()) > 0){
        String key =  taskinfo.getTaskType() + "_" + taskinfo.getPriority();
        /**
         * 执行时间超过当前日期直接加入到未来任务执行队列当中
         */
        cacheService.zAdd(ScheduleConstants.FUTURE + key, JSON.toJSONString(taskinfo),taskinfo.getExecuteTime().getTime());
    }
    TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
    BeanUtils.copyProperties(taskinfo, taskinfoLogs);
    taskinfoLogs.setVersion(1);
    taskinfoLogs.setStatus(ScheduleConstants.TaskStatus.INIT);
    //保存到日志表中
    taskInfoLogsService.save(taskinfoLogs);
    return taskinfo.getTaskId();
}

如果小于当前系统时间(任务补偿机制。这种情况有可能出现在于由于微服务架构中一般定时任务被单独抽取成为一个模块,模块中通信可能基于消息队列,对于消息阻塞的情况可能会出现定时任务时间小于当前时间),直接存储到Redis的Zset集合中,并设置Score为当前时间戳(只占用4个字节 时间从 1970-01-01 到 2060年),否则则存入数据库中。

2.数据库表结构,对于定时任务我设计了两张表,一张表为taskInfo,另外一张为taskInfoLog表。日志表的作用在于记录任务是否完成,用于排查异常并且对未执行的任务进行补偿机制。

taskInfoLog表

3.回到代码层面,这个时候我们使用Java提供的定时任务每5分钟从数据库中查询[当前时间,当前时间+5min]区间内的任务,这里推荐大家使用日历类Calendar来添加时间,Date类中的方法已经过时。

注意这里必须使用分布式锁,由于我们项目是多中心部署,每个中心都有16台主机。如果不加分布式锁可能会出现多台主机同时拉取任务的情况没必要且浪费性能(当然不用担心拉取重复任务的情况,Zset结构自动去重不会出现相同的任务)

/**
 * 五分钟执行一次从数据库中得到数据存到redis中
 */
@Scheduled(cron = "0 0/5 * * * ?")
public void schedule(){
  	//尝试获取分布式锁
    String lock = cacheService.tryLock(ScheduleConstants.LOCK_NAME, 3000);
    if (Strings.isBlank(lock)){
        log.info("当前主机没有抢占到redis分布式锁,故不执行刷新定时任务");
        return;
    }
    log.info("-----------从数据库中查询任务存入redis中-----------");
    Calendar future = Calendar.getInstance();
    future.add(Calendar.MINUTE, 5);
    LambdaQueryWrapper<Taskinfo> taskinfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
    //时间区间为:[当前时间,当前时间+5分钟]
    LambdaQueryWrapper<Taskinfo> queryWrapper = taskinfoLambdaQueryWrapper.le(Taskinfo::getExecuteTime, future.getTime())
            .ge(Taskinfo::getExecuteTime, new Date());
    List<Taskinfo> taskinfos = taskInfoMapper.selectList(queryWrapper);
    if (!taskinfos.isEmpty()){
        //找出了任务
        //存入redis的zset集合中去
        log.info("从DB中查询出来的需要放入redis中未来队列的数据有:{}",taskinfos);
        for (Taskinfo taskinfo : taskinfos) {
            //存入zset集合中去
            String key = taskinfo.getTaskType() + "_" + taskinfo.getPriority();
            cacheService.zAdd(ScheduleConstants.FUTURE + key,JSON.toJSONString(taskinfo),System.currentTimeMillis());
        }
    }
    log.info("-----------从数据库中查询任务存入redis中结束-----------");
}

4.任务加入到future队列后等待被执行,我们每分钟到redis里面查询需要执行的任务,使用redis的zRangeOfScore方法可以保证任务执行时间误差控制在1分钟内,使用这个方法可以获取当前时间到未来一分钟内需要执行的任务

将得到的任务通过redis管道存入List执行队列中,并且从原先zset队列中删除原有的任务,redis的管道效率实在太高辣!!!!!

大家可以自己取测试一下,我做过一个测试向list中加入相同1000条数据的情况下,使用管道技术效率高了接近20倍

@Scheduled(cron = "0 0/1 * * * ? ")
public void refreshRedisTask(){
    log.info("---------------刷新future队列中的任务开始------------");
    //模糊查询所有未来任务队列
    Set<String> likeKey = cacheService.scan(ScheduleConstants.FUTURE + "*");
    likeKey.forEach(key ->{
        //从futureKey中得到所有
        Set<String> taskKeys = cacheService.zRange(key, System.currentTimeMillis(), System.currentTimeMillis() + 1 * 60 * 1000);
        if (taskKeys.isEmpty()){
            log.info("当前未来任务队列中没有任务");
            return;
        }
        String consumerKey = ScheduleConstants.CONSUMER + key.substring(key.indexOf("_") + 1);
        String futureKey = ScheduleConstants.FUTURE + key.substring(key.indexOf("_") + 1);
        log.info("取出{}条任务转存到消费队列中去:{}", taskKeys.size(),taskKeys);
        cacheService.refreshWithPipeline(futureKey,consumerKey,taskKeys);
    });
}

后续就简单了在需要消费任务的地方使用监听器监听list队列消费任务即可

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis实现定时任务 的相关文章

  • Redis 写入 .ssh/authorized_keys

    当前设置 2 个主服务器 12 个工作服务器 工作人员通过 ssh copy id 连接到主设备 主设备和工作人员正在主设备上的 redis 队列中写入数据 过去一周我遇到的问题是 Redis 正在将数据写入authorized keys
  • AWS Redis 从外部连接

    有没有办法从外部 AWS 网络连接 AWS 上托管的 Redis 实例 我有一个基于 Windows 的 EC2 实例在 AWS 上运行 另一个是 Redis 缓存节点 我知道有人问过这个问题 但答案是在基于 Linux 的系统中 但我的是
  • 使用brew在MacOSx上安装Redis JSON

    如何使用brew 在 macOSx 上安装 RedisJSON 如何在不编译redis的情况下启用redis上的模块 我不想使用 docker 客户端 Redis Stack 可能是最简单的方法 它不仅仅是 RedisJSON 还包括 Re
  • 保护节点 Redis

    我正在尝试保护 Node Redis IPC 服务器以使用私钥 公钥 我已经关注了本教程 http bencane com 2014 02 18 sending redis traffic through an ssl tunnel wit
  • Stackexchange.redis 缺乏“WAIT”支持

    我在客户端应用程序正在使用的负载均衡器后面有 3 个 Web API 服务器 我正在使用这个库来访问具有一个主服务器和几个从服务器的 Redis 集群 目前不支持 WAIT 操作 我需要此功能来存储新创建的用户会话并等待它复制到所有从属服务
  • PooledRedisClientManager 未释放连接

    我将 json 数据列表存储在 redis 中并使用 ServiceStack c 客户端访问它 我本质上是在管理自己的外键 我在其中存储zrangeid 我使用应用程序内部的接口从zrange然后从 Redis 获取底层 json 对象并
  • 如何让客户端下载动态生成的非常大的文件

    我有一个导出功能 可以读取整个数据库并创建一个包含所有记录的 xls 文件 然后文件被发送到客户端 当然 导出完整数据库的时间需要大量时间 并且请求很快就会以超时错误结束 处理这种情况的最佳解决方案是什么 例如 我听说过使用 Redis 创
  • 如何使用Spring Cache处理redis异常?

    我目前正在开发一个包含 Spring Data Redis 和 Spring Cache 的项目 在spring data redis中 我使用redis模板调用redis 我在 try catch 块中处理 redis 模板抛出的所有异常
  • 为什么Redis中不建议使用KEYS?

    在Redis中 建议不要使用按键命令 https redis io commands KEYS 为什么会这样呢 是因为它的时间复杂度是 O N 吗 或者是别的什么原因 我做了下面的实验来证明KEYS命令有多么危险 当带有 KEYS 的一个命
  • 如何设置 Celery 以通过 ssl 与 Azure Redis 实例对话

    使用 的伟大答案 如何在microsoft azure上的django项目中配置celery redis https stackoverflow com questions 39616701 how to configure celery
  • Docker-compose Predis 不通过 PHP 连接

    我正在尝试使用 docker compose 将 PHP 与 redis 连接 docker compose yml version 2 services redis image redis 3 2 2 php image company
  • Redis INCRBY 有限制

    我想知道是否有一种方法可以通过我的应用程序的单次往返在 Redis 中执行此操作 对于给定的键K 其可能值V是范围内的任意整数 A B 基本上 它有上限和下限 When an INCRBY or DECRBY发出命令 例如INCRBY ke
  • 通过 StackExchange.Redis 连接到 Redis Servier

    我尝试使用以下方法制作一个测试项目Redis https redis io服务器 通过 Virtual Box 安装在 Linux Ubuntu 虚拟机上 Linux 机器通过 Virtual Box 的桥接适配器与本地网络连接 Virtu
  • 在 sidekiq 上配置 redis 身份验证

    我想我错过了一些东西 因为我在文档中找不到如何编写 redis 实例的用户名和密码以与 sidekiq 一起使用 有没有办法做到这一点 或者是通过 ENV 变量 Sidekiq 将无法识别的 Redis 选项直接传递给 Redis 驱动程序
  • Lua中按字符分割字符串

    我有像这样的字符串 ABC DEF 我需要将它们分开 字符并将两个部分分别分配给一个变量 在 Ruby 中 我会这样做 a b ABC DEF split 显然Lua没有这么简单的方法 经过一番挖掘后 我找不到一种简短的方法来实现我所追求的
  • Redis Cluster 与 Pub/Sub 中的 ZeroMQ,用于水平扩展的分布式系统

    如果我要设计一个巨大的分布式系统 其吞吐量应随系统中的订阅者数量和通道数量线性扩展 哪个会更好 1 Redis集群 仅适用于Redis 3 0 alpha 如果是集群模式 您可以在一个节点上发布并在另一个完全不同的节点上订阅 消息将传播并到
  • Spring Data JPA Redis:无法编写基于自定义方法的查询

    我已经使用 Redis 配置了 Spring Data JPA 并使用RedisRepositorieswith 提供了类似的方法find findAll 所有这些方法似乎都工作得很好 但我无法编写我的自定义方法 RedisEntity f
  • Redis是如何实现高吞吐量和高性能的?

    我知道这是一个非常普遍的问题 但是 我想了解允许 Redis 或 MemCached Cassandra 等缓存 以惊人的性能极限工作的主要架构决策是什么 如何维持连接 连接是 TCP 还是 HTTP 我知道它完全是用C写的 内存是如何管理
  • 如何使 Redis 缓存中数据层次结构(树)的部分内容无效

    我有一些产品数据 需要在 Redis 缓存中存储多个版本 数据由 JSON 序列化对象组成 获取普通 基本 数据的过程很昂贵 将其定制为不同版本的过程也很昂贵 因此我想缓存所有版本以尽可能进行优化 数据结构看起来像这样 BaseProduc
  • redis dump.rdb / 保存小文件

    Context 我正在使用redis 数据库小于 100 MB 但是 我想进行每日备份 我也在 Ubuntu Server 12 04 上运行 当输入 redis cli save 我不知道 dump rdb 保存到哪里 因为 redis

随机推荐

  • Linux基础命令1(农夫笔记-自用)

    1 创建和查看隐藏文件 以 开头的文件或文档 攻击者可能要去传一个木马或者后门 或者自己要去做这些事情的时候需要用到 2 cd 等于cd 一样的作用 可以回到家目录 3 ll是ls l的别名 也具有同样的效果 就是使用较长格式列出信息 4
  • MATLAB中如何输入特殊符号

    alpha beta gamma theta Theta Gamma delta Delta xi Xi elta epsilong zeta miu nu tau lamda Lamda pi Pi sigma Sigma phi Phi
  • 初识express/路由/中间件

    路由的概念 模块化路由 中间件 要有输入输出 简化版本 全局生效中间件 局部生效中间件 注意事项 中间件分类 内置中间件 解析请求体 url encoded 自定义中间件 使用querystring模块解析请求体数据 编写接口
  • Linux笔记

    文章目录 一 初识Linux 1 操作系统概述 1 1 计算机由哪两个主要部分组成 1 2 操作系统是什么 有什么作用 1 3 常见的操作系统有哪些 2 Linux初识 3 虚拟机介绍 3 1 什么是虚拟机 3 2 为什么要使用虚拟机 4
  • 小程序扫码只允许扫码运单条形码

    wx scanCode success res gt console log res let result res result 只允许 字母 数字 字母 数字 if result 0 9a zA Z g test result wx sh
  • [网络安全自学篇] 二十七.Sqlmap基础知识、CTF实战及请求参数设置(一)

    这是作者的系列网络安全自学教程 主要是关于网安工具和实践操作的在线笔记 特分享出来与博友共勉 希望您们喜欢 一起进步 前文分享了Shodan搜索引擎的基本用法及Python命令行 本篇文章详细讲解了Sqlmap的基本用法 CTF实战 并且分
  • 由浅及深PCB布线设计

    第一部分 在当今激烈竞争的电池供电市场中 由于成本指标限制 设计人员常常使用双面板 尽管多层板 4层 6层及8层 方案在尺寸 噪声和性能方面具有明显优势 成本压力却促使工程师们重新考虑其布线策略 采用双面板 在本文中 我们将讨论自动布线功能
  • 动态规划——数字三角形C语言

    一 分析 先说一下相关动态规划的一些概念 参考下方博文 原文链接 https blog csdn net every day article details 88174082 一个模型三个特征 理论的讲解 动态规划作为一个非常成熟的算法思想
  • Sentence-BERT论文阅读笔记

    目录 1 第一篇论文 Sentence BERT Sentence Embeddings using Siamese BERT Networks 1 1 论文基本信息 1 2 动机 1 3 模型 1 4 实验 1 4 1 训练所用的数据集
  • css如何实现三角形的方法

    在开发页面的时候 遇到很多的列表都需要用到箭头 可以直接用图片作背景铺垫 纯CSS也能实现 并且没有兼容性顾虑 不用CSS3 相比而言 比图片更好用 原理 一个高宽相等的正方形 选取你所需要的某一边 截取之 就是一个梯形 当高宽都为0 且其
  • springboot日志操作

    springboot日志 日志介绍 1 日志的输出等级 2 运用lombok快速添加日志对象log 3 日志输出格式控制 4 日志文件 4 1 直接指定文件名 4 2 指定最大空间和格式 日志介绍 日志就是记录程序日常运行的信息 sprin
  • Android中图片的裁剪与压缩

    文章目录 一 图片的剪裁 1 属性介绍 二 图片压缩 1 图片质量分类 2 图片默认质量 3 占用内存 4 图片的尺寸压缩或者拉伸 三 Bitmap压缩 1 质量压缩 2 采样率压缩 3 缩放法压缩 一 图片的剪裁 ImageView默认的
  • error: ‘for’ loop initial declarations are only allowed in C99 mode (改Makefile)

    c 强制C99在CMake 使用 for 循环初始声明 2019 04 25 C C 编程之家 https www jb51 cc 编程之家收集整理的这篇文章主要介绍了c 强制C99在CMake 使用 for 循环初始声明 编程之家小编觉得
  • uCOSii中的互斥信号量

    uCOSii中的互斥信号量 一 互斥型信号量项管理 MUTUAL EXCLUSION SEMAPHORE MANAGEMENT OSMutexAccept 无条件等待地获取互斥型信号量 OSMutexCreate 建立并初始化一个互斥型信号
  • Peewee

    Part1前言 在 Python 的 ORM 框架中 比较主流的有 Sqlalchemy peewee pony 等等 但是其中 peewee 和 Django 的 Models 框架很像 如果了解 Django 的同学肯定对 peewee
  • mybatis 返回结果为Map

    表 members 中的gender 列是这样 Female Female Male Female Male Male Male Male Male 现在我们想统计 members 中男女人数分别是多少 很显然返回的结果应该是这样的 Fem
  • No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalanc

    1 报错原因 启动SpringCloud项目时报 No Feign Client for loadBalancing defined Did you forget to include spring cloud starter loadba
  • hooks实践总结

    何为hooks 在React中hook是指不编写 class 的情况下使用 state 以及其他的 React 特性 而Vue3也推出了具有相同功能的组合式API 如果你用过Vue3就会知道在 setup 中你应该避免使用 this 因为h
  • 汇编语言(第三版)读书笔记 2 - 第2章 寄存器

    第2章 寄存器 前一章所说的总线 相对于CPU内部来说是外部总线 内部总线实现了CPU内部各个器件 运算器 控制器 寄存器 之间的联系 外部总线实现了CPU和主板上其他器件的联系 不同的CPU 寄存器的个数 结构是不相同的 8086 CPU
  • Redis实现定时任务

    Redis定时任务的核心在于 Schedule 注解 Redis Zset List数据结构 Redis管道技术 就从定时任务的执行流程开始写起 1 前端用户发起定时任务创建定时任务任务 像定时任务模块发起定时任务请求并且携带必要参数 首先