spring动态控制定时任务
在spring框架中,对于简单的定时任务,可以使用 @Scheduled 注解实现,在实际项目中,经常需要动态的控制定时任务,比如通过接口增加、启动、停止、删除定时任务,动态的改变定时任务的执行时间等。
我们可以通过编码的方式动态控制定时任务,具体的代码参照 示例项目 https://github.com/qihaiyan/springcamp/tree/master/spring-dynamic-scheduler
一、概述
在spring框架可以通过 CronTask 和 TaskScheduler 动态控制定时任务,实现定时任务的动态更新,比如修改定时任务的执行时间,这个是 @Scheduled 无法实现的。采用编码控制动态任务的方式,我们还可以把动态任务执行信息保存到数据库中,通过数据库里的任务配置数据来动态控制定时任务,也可以通过接口来动态控制定时任务。
二、配置定时任务
首先,同 @Scheduled 注解的方式一样,动态控制定时任务也需要使用 @EnableScheduling 注解来开启定时任务功能:
然后通过实现 SchedulingConfigurer 接口来对动态任务进行配置:
@Component
public class MyScheduler implements SchedulingConfigurer {private ScheduledTaskRegistrar taskRegistrar;private final ConcurrentHashMap<Long, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();@Overridepublic void configureTasks(@NonNull ScheduledTaskRegistrar taskRegistrar) {ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();threadPoolTaskScheduler.setPoolSize(10);// Set the pool of threadsthreadPoolTaskScheduler.setThreadNamePrefix("sys-scheduler");threadPoolTaskScheduler.initialize();this.taskRegistrar = taskRegistrar;this.taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);}@PreDestroypublic void destroy() {this.taskRegistrar.destroy();}
}
通过上面的代码,我们就启用了动态任务的基本能力,为动态任务指定了执行线程池。
三、动态更新定时任务
更新定时任务通过 CronTask 和 TaskScheduler 来实现,我们新增一个注册定时任务的方法:
public void registerTask(TaskData taskData) {//如果配置一致,则不需要重新创建定时任务if (scheduledFutures.containsKey(taskData.getId())&& cronTasks.get(taskData.getId()).getExpression().equals(taskData.getExpression())) {return;}//如果策略执行时间发生了变化,则取消当前策略的任务if (scheduledFutures.containsKey(taskData.getId())) {scheduledFutures.remove(taskData.getId()).cancel(false);cronTasks.remove(taskData.getId());}CronTask task = new CronTask(taskData, taskData.getExpression());TaskScheduler scheduler = taskRegistrar.getScheduler();if (scheduler != null) {ScheduledFuture<?> future = scheduler.schedule(task.getRunnable(), task.getTrigger());if (future != null) {scheduledFutures.put(taskData.getId(), future);}}}
我们新增了一个 registerTask 方法用于注册定时任务,入参中 TaskData 是定时任务的配置数据,为了简单,我们把配置数据和执行代码放到了一起:
@Slf4j
@Data
@Entity
public class TaskData implements Runnable {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String expression;@Transient@Overridepublic void run() {log.info("{} is running with expression {}", this.getName(), this.getExpression());}
}
核心代码是创建一个 CronTask 对象,该对象包含两个参数:Runnable 方法和 cron 表达式。
CronTask 对象创建好后,通过 ScheduledTaskRegistrar 对定时任务进行注册,注册完成后,定时任务就会在cron表达式指定的时间点开始执行了。
执行的代码就是 Runnable 参数指定的方法。
四、动态停止定时任务
为了能够动态停止定时任务,我们在注册定时任务时,把注册结果放到了一个Map中:
private final ConcurrentHashMap<Long, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>();ScheduledFuture<?> future = scheduler.schedule(task.getRunnable(), task.getTrigger());if (future != null) {scheduledFutures.put(taskData.getId(), future);}
新增停止定时任务的方法:
public void stop(Long id) {if (scheduledFutures.containsKey(id)) {scheduledFutures.remove(id).cancel(false);}}
该方法需要传入定时任务的id,由于我们把定时任务信息保存到了 scheduledFutures 这个Map中,所以可以根据任务id参数查找到对应的定时任务信息,然后调用对应的 cancel方法来停止定时任务。
五、通过接口控制定时任务
通过上面的步骤我们已经具备了动态控制定时任务的基本能力,下面增加接口来控制定时任务:
@EnableScheduling
@SpringBootApplication
@RestController
public class DemoApplication {@Autowiredprivate MyScheduler myScheduler;@Autowiredprivate TaskDataRepository taskDataRepository;public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@RequestMapping("/register")public TaskData register(String name,@RequestParam(name = "expression", required = false, defaultValue = "0/1 * * * * ?") String expression) {TaskData taskData = taskDataRepository.findOneByName(name).orElse(new TaskData());taskData.setName(name);taskData.setExpression(expression);taskData = taskDataRepository.save(taskData);myScheduler.registerTask(taskData);return taskData;}@RequestMapping("/stop")public void stop(Long id) {taskDataRepository.findById(id).ifPresent(taskData -> {myScheduler.stop(id);});}
}
我们提供了 register 和 stop 两个接口,这两个接口会在改变动态任务执行数据时,先将数据保存到数据库中,对定时任务进行持久化,避免程序重启后定时任务都丢失。
程序启动后,我们首先调用 register 接口新增一个定时任务:
http://localhost:8080/register?name=test
接口调用后,在日志中可以看到定时任务开始执行了:
2024-01-07T18:02:09.003+08:00 INFO 23012 --- [ sys-scheduler5] c.s.springdynamicscheduler.TaskData : test is running with expression 0/1 * * * * ?
2024-01-07T18:02:10.005+08:00 INFO 23012 --- [ sys-scheduler3] c.s.springdynamicscheduler.TaskData : test is running with expression 0/1 * * * * ?
2024-01-07T18:02:11.012+08:00 INFO 23012 --- [ sys-scheduler3] c.s.springdynamicscheduler.TaskData : test is running with expression 0/1 * * * * ?
再调用 stop 接口,通过日志可以发现定时任务停止了执行:
http://localhost:8080/stop?id=1
相关文章:
spring动态控制定时任务
在spring框架中,对于简单的定时任务,可以使用 Scheduled 注解实现,在实际项目中,经常需要动态的控制定时任务,比如通过接口增加、启动、停止、删除定时任务,动态的改变定时任务的执行时间等。 我们可以通过…...
3. Mybatis 中SQL 执行原理
2. Mybatis 中SQL 执行原理 这里有两种方式,一种为常用的 Spring 依赖注入 Mapper 的方式。另一种为直接使用 SqlSessionTemplate 执行 Sql 的方式。 Spring 依赖注入 Mapper 的方式 Mapper 接口注入 SpringIOC 容器 Spring 容器在扫描 BeanDefinition 阶段会扫…...
第一次在RUST官方论坛上留言发布我的Rust板箱
第一次在RUST官方论坛上发帖子,有点紧张~地址在这里: 【My Rust Crate】obtains linux local information - The Rust Programming Language Forum (rust-lang.org)...
LabVIEW 智能化矿用定向钻机液压系统监测
简介 在矿用定向钻机的液压系统监测中,实现实时监控和异常预警对于保障设备运行的稳定性至关重要。传统的人工监测方法效率低下而且准确性不能满足要求,针对这种情况采用 LabVIEW 开发平台,设计并实现了一套智能化矿用定向钻机液压系统的状态…...
GO数据库操作
Golang 出色的 ORM 库为 GORM。 官网文档:https://gorm.io/docs/ 我们来说说增删改查的用法,更深入的研究可以去官网看看。 GORM功能概览: 关联(有一个、有多个、属于、多对多、多态性、单表继承)挂钩(创…...
PyTorch简单理解ChannelShuffle与数据并行技术解析
目录 torch.nn子模块详解 nn.ChannelShuffle 用法与用途 使用技巧 注意事项 参数 示例代码 nn.DataParallel 用法与用途 使用技巧 注意事项 参数 示例 nn.parallel.DistributedDataParallel 用法与用途 使用技巧 注意事项 参数 示例 总结 torch.nn子模块详…...
MySQL 8查询语句之查询所有字段、特定字段、去除重复字段、Where判断条件
《MySQL 8创建数据库、数据表、插入数据并且查询数据》里边有我使用到的数据。 再使用下方的语句补充一些数据: insert into Bookbought.bookuser(id,username,userphone,userage,sex,userpassword) values (11,Book Break,22245678911,18,male,good#111); insert…...
LLaMA-Factory添加adalora
感谢https://github.com/tsingcoo/LLaMA-Efficient-Tuning/commit/f3a532f56b4aa7d4200f24d93fade4b2c9042736和https://github.com/huggingface/peft/issues/432的帮助。 在LLaMA-Factory中添加adalora 1. 修改src/llmtuner/hparams/finetuning_args.py代码 在FinetuningArg…...
多端多用户万能DIY商城系统源码:自营+多商户入驻商城系统 独立部署 带完整的安装代码包以及搭建教程
电子商务行业日新月异,许多企业希望能够通过线上商城拓展业务。但是,传统商城系统往往无法满足多样化、个性化的需求,而且开发周期长、成本高。罗峰就来给大家分享一款多端多用户万能DIY商城系统源码,搭建简单。 以下是部分代码示…...
Qt 6之七:学习资源
Qt 6之七:学习资源 Qt是一种跨平台的C应用程序开发框架,它提供了一套丰富的工具和库,可以帮助开发者快速构建跨平台的应用程序,用于开发图形用户界面(GUI)和非GUI应用程序。 Qt 6之一:简介、安…...
解决大模型的幻觉问题:一种全新的视角
在人工智能领域,大模型已经成为了一个重要的研究方向。然而,随着模型规模的不断扩大,一种新的问题开始浮出水面,那就是“幻觉”问题。这种问题的出现,不仅影响了模型的性能,也对人工智能的发展带来了新的挑…...
mysql进阶-重构表
目录 1. 原因 2. 如何重构表呢? 2.1 命令1: 2.2 命令2: 2.3 命令3: 1. 原因 正常的业务开发,为什么需要重构表呢? 原因1:某张表存在大量的新增和删除操作,导致表经历过大量的…...
Element-ui图片懒加载
核心代码 <el-image src"https://img-blog.csdnimg.cn/direct/2236deb5c315474884599d90a85d761d.png" alt"我是图片" lazy><img slot"error" src"https://img-blog.csdnimg.cn/direct/81bf096a0dff4e5fa58e5f43fd44dcc6.png&quo…...
Linux系统——DNS解析详解
目录 一、DNS域名解析 1.DNS的作用 2.域名的组成 2.1域名层级结构关系特点 2.2域名空间构成 2.3域名的四种不同类型 2.3.1延伸 2.3.2总结 3.DNS域名解析过程 3.1递归查询 3.2迭代查询 3.3一次DNS解析的过程 4.DNS系统类型 4.1缓存域名服务器 4.2主域名服务器 4…...
初识Ubuntu
其实还是linux操作系统 命令都一样 但是在学习初级阶段,我还是将其分开有便于我的学习和稳固。 cat 查看文件 命令 Ubuntu工作中经常是用普通用户,在需要时才进行登录管理员用户 sudn -i 切换成管理用户 我们远程连接时 如果出现 hostname -I没有出现…...
Casper Network (CSPR)2024 年愿景:通过投资促进增长
Casper Network (CSPR)是行业领先的 Layer-1 区块链网络之一,通过推出了一系列值得关注的技术改进和倡议,已经为 2024 年做好了准备。 在过去的一年里,Casper Network (CSPR)不断取得里程碑式的进展,例如推…...
《MySQL系列-InnoDB引擎06》MySQL锁介绍
文章目录 第六章 锁1 什么是锁2 lock与latch3 InnoDB存储引擎中的锁3.1 锁的类型3.2 一致性非锁定读3.3 一致性锁定读3.4 自增长与锁3.5 外键和锁 4 锁的算法4.1 行锁的三种算法4.2 解决Phantom Problem 5 锁问题5.1 脏读5.2 不可重复读5.3 丢失更新 6 阻塞7 死锁 第六章 锁 开…...
获取多个PDF文件的内容并保存到excel上
# shuang # 开发时间:2023/12/9 22:03import pdfplumber import re import os import pandas as pd import datetimedef re_text(bt, text):# re 搜索正则匹配 包含re.compile包含的文字内容m1 re.search(bt, text)if m1 is not None:return re_block(m1[0])return…...
深入了解网络流量清洗--使用免费的雷池社区版进行防护
随着网络攻击日益复杂,企业面临的网络安全挑战也在不断增加。在这个背景下,网络流量清洗成为了确保企业网络安全的关键技术。本文将探讨雷池社区版如何通过网络流量清洗技术,帮助企业有效应对网络威胁。 ![] 网络流量清洗的重要性&#x…...
【FFMPEG应用篇】基于FFmpeg的转码应用(FLV MP4)
方法声明 extern "C" //ffmpeg使用c语言实现的,引入用c写的代码就要用extern { #include <libavcodec/avcodec.h> //注册 #include <libavdevice/avdevice.h> //设备 #include <libavformat/avformat.h> #include <libavutil/…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...
uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...
密码学基础——SM4算法
博客主页:christine-rr-CSDN博客 专栏主页:密码学 📌 【今日更新】📌 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 编辑…...
