最近,有个项目有需要用到定时任务,所以做了一个动态管理定时任务的模块。本文将从项目背景、需求、选型、思路、具体实现等方面展开介绍。

背景:有个支付类的项目,中间会产生一些中间态的订单,需要有个定时任务轮询确认订单状态。该类项目体量较小,单节点部署,客户比较多,需要简单快速的部署、维护。

需求:定时任务能够通过表达式灵活指定执行计划,并支持动态启动、关闭、修改。定时任务模块最好和业务包在一个jar包内,部署简单。

选型:说到定时任务,当下最火的当属xxl-job,本案为什么不采用xxl-job呢?不是因为它不够强大,是因为需要单独部署组件,并且需要建一系列相关的表,运维小哥哥不会弄或者嫌麻烦。基于上述原因,考虑采用Springboot自己的定时任务,常见的有两种实现方式,一种基于注解,如:

@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class SaticScheduleTask {@Scheduled(cron = "0/5 * * * * ?")private void configureTasks() {System.out.println("开始执行静态定时任务时间");}
}

另一种基于接口,主要代码如:

@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class DynamicScheduleTask implements SchedulingConfigurer {/*** 执行定时任务.*/@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.addTriggerTask(//1.添加任务内容(Runnable)() -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),//2.设置执行周期(Trigger)triggerContext -> {String cron = "0/5 * * * * ?";//这个表达式可以写在配置文件里,也可以从数据库读取//合法性校验if (StringUtils.isEmpty(cron)) {//这里写具体的业务代码}//返回执行周期(Date)return new CronTrigger(cron).nextExecutionTime(triggerContext);});}}

但是上述两种都不太灵活,不能动态的启动、关闭和修改。最后,选择通过ThreadPoolTaskScheduler来实现动态管理定时任务。

思路:

1、ThreadPoolTaskScheduler可以实现任务调度,支持基于cron表达式的任务,其提交任务接口如下:

public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {//...}

那么,定时任务业务实现类实现Runnable接口,用cron构造CronTrigger,就可以完成任务提交。提交任务的时候将回调对象ScheduledFuture记录下来,后面可以用它的cancel方法实现任务的关闭。

2、在数据库建一张任务表,主要有任务编号、任务名称、调度计划表达式、任务状态等字段。在项目启动后,自动查询任务表,加载任务状态为启动状态的任务。

3、写一个前端管理页面,主要用于查询展示定时任务,新增、启动、关闭和修改定时任务。通过管理页面对任务进行启动、关闭等操作时,一方面修改数据库中的状态,另一方面也要同步做启动或者关闭任务操作。

具体实现:

核心代码:

@Component
public class DynamicTimedTask implements ApplicationRunner {@AutowiredTimedTaskService timedTaskService;private static final Logger logger = LoggerFactory.getLogger(DynamicTimedTask.class);/*** @Author yrd* @Description 项目启动后自动加载数据库里状态为启动的定时任务* @Date 10:40 2021/12/20* @Param [args]* @return void**/@Overridepublic void run(ApplicationArguments args) throws Exception {//查询数据库中定时任务信息List<Map> timedTasks = timedTaskService.queryAllTimedTask();for (Map timedTask : timedTasks) {String taskId = timedTask.getString("taskid");//任务编号String cron = timedTask.getString("cron");//表达式String status = timedTask.getString("status");//任务状态if ("1".equals(status)) {startTask(taskId, cron);}}}//接受任务的返回结果private ConcurrentHashMap<String, ScheduledFuture> futureMap = new ConcurrentHashMap<>();@Autowiredprivate ThreadPoolTaskScheduler threadPoolTaskScheduler;//实例化一个线程池任务调度类,可以使用自定义的ThreadPoolTaskScheduler@Beanpublic ThreadPoolTaskScheduler threadPoolTaskScheduler() {ThreadPoolTaskScheduler schedulerPool = new ThreadPoolTaskScheduler();schedulerPool.setPoolSize(3);schedulerPool.setWaitForTasksToCompleteOnShutdown(true);schedulerPool.setAwaitTerminationSeconds(60);return schedulerPool;}/*** 启动定时任务* @return*/public boolean startTask(String taskId, String cron) {boolean flag = false;ScheduledFuture future = futureMap.get(taskId);if (future != null) {if (future.isCancelled()) {logger.info("任务【{}】已存在但是关闭状态!!!", taskId);} else {logger.info("任务【{}】已存在且已启动!!!", taskId);return true;}}if (SpringBeanUtils.containsBean(taskId)) {future = threadPoolTaskScheduler.schedule((Runnable) SpringBeanUtils.getBean(taskId), new CronTrigger(cron));if (future != null){flag = true;futureMap.put(taskId, future);logger.info("任务【{}】启动成功!!!", taskId);}else {logger.info("任务【{}】启动失败!!!", taskId);}} else {logger.info("任务【{}】未实现!!!", taskId);}return flag;}/*** 停止定时任务* @return*/public boolean stopTask(String taskId) {boolean flag = false;ScheduledFuture future = futureMap.get(taskId);if (future == null) {logger.info("任务【{}】不在任务队列中!!!", taskId);flag = true;} else {if (future.isCancelled()) {logger.info("任务【{}】已经是关闭状态!!!", taskId);flag = true;} else {boolean cancel = future.cancel(true);if (cancel){flag = true;logger.info("任务【{}】关闭成功!!!", taskId);}else {logger.info("任务【{}】关闭失败!!!", taskId);}}}return flag;}
}

说明:上述代码用到了通过bean的ID从应用上下文中获取bean实例,具体实现方法网上有很多介绍,本文不再详述。因此,具体任务的业务实现类bean的ID需要与任务ID一致,上述代码中startTask方法也对次作了判断。如图:

任务管理页面对应的controller类:

@Controller
@RequestMapping("/timedTask")
public class TimedTaskManageController {@AutowiredTimedTaskService timedTaskService;/*** @Author yrd* @Description 进入定时任务管理页面* @Date 10:37 2021/12/20* @Param []* @return java.lang.String**/@RequestMapping("/timedTaskManagePage")public String timedTaskManagePage() throws AppException {return "/timedTask/timedTaskManage";}/*** @Author yrd* @Description 查询定时任务* @Date 10:38 2021/12/20* @Param [para]* @return java.util.List**/@RequestMapping("/queryTimedTask")@ResponseBodypublic List queryTimedTask(@RequestBody DataObject para) throws Exception {return timedTaskService.queryTimedTask(para);}/*** @Author yrd* @Description 进入新增定时任务页面* @Date 10:38 2021/12/20* @Param []* @return java.lang.String**/@RequestMapping("/addTimedTaskPage")public String addTimedTaskPage() throws Exception {return "/timedTask/addTimedTask";}/*** @Author yrd* @Description 根据任务编号检查定时任务* @Date 10:38 2021/12/20* @Param [taskId]* @return java.lang.String**/@RequestMapping("/checkTimedTask")@ResponseBodypublic String checkTimedTask(@RequestParam("taskId") String taskId) throws Exception {Map map = timedTaskService.queryTimedTaskById(taskId);JSONObject object = new JSONObject();if (map.isEmpty()) {if (SpringBeanUtils.containsBean(taskId)) {object.put("success", "true");object.put("msg", "");} else {object.put("success", "false");object.put("msg", "该任务编号对应的任务未实现!");}} else {object.put("success", "false");object.put("msg", "任务编号重复!");}return object.toString();}/*** @Author yrd* @Description 新增定时任务* @Date 10:39 2021/12/20* @Param [request]* @return java.lang.String**/@RequestMapping("/addTimedTask")@ResponseBodypublic String addTimedTask(HttpServletRequest request) throws Exception {Map param = new HashMap();param.put("taskId", request.getParameter("taskId").toString());param.put("taskName", request.getParameter("taskName").toString());param.put("cron", request.getParameter("cron").toString());return timedTaskService.addTimedTask(param);}/*** @Author yrd* @Description 进入修改定时任务页面* @Date 10:39 2021/12/20* @Param [taskId, model]* @return java.lang.String**/@RequestMapping("/editTimedTaskPage")public String editTimedTaskPage(@RequestParam("taskId") String taskId , Model model) throws Exception {Map map = timedTaskService.queryTimedTaskById(taskId);model.addAttribute("param", map);return "/timedTask/editTimedTask";}/*** @Author yrd* @Description 启动定时任务* @Date 10:39 2021/12/20* @Param [taskId]* @return java.lang.String**/@RequestMapping("/startTask")@ResponseBodypublic String startTask(@RequestParam("taskId") String taskId) throws Exception {return timedTaskService.startTask(taskId);}/*** @Author yrd* @Description 关闭定时任务* @Date 10:39 2021/12/20* @Param [taskId]* @return java.lang.String**/@RequestMapping("/stopTask")@ResponseBodypublic String stopTask(@RequestParam("taskId") String taskId) throws Exception {return timedTaskService.stopTask(taskId);}/*** @Author yrd* @Description 修改定时任务* @Date 10:39 2021/12/20* @Param [request]* @return java.lang.String**/@RequestMapping("/editTimedTask")@ResponseBodypublic String editTimedTask(HttpServletRequest request) throws Exception {Map param = new HashMap();param.put("taskId", request.getParameter("taskId").toString());param.put("taskName", request.getParameter("taskName").toString());param.put("cron", request.getParameter("cron").toString());return timedTaskService.editTimedTask(param);}
}

前端、service及dao层的对应代码本文不再详述,以上功能亲测可以使用,并已运用到实际项目当中去。

ThreadPoolTaskScheduler实现动态管理定时任务相关推荐

  1. quartz 动态添加job_SpringBoot+Quartz实现动态管理定时任务

    前言: 最近在做Java开发对接微信公众平台之类的工作,在开发过程中遇到一个需求是定时任务这块的,但是普通的定时任务还远远不能满足:最后要实现的效果是每个任务都是不同的定时时间来执行而且是在前台页面上 ...

  2. boot-admin整合Quartz实现动态管理定时任务

    淄博烧烤爆红出了圈,当你坐在八大局的烧烤摊,面前是火炉.烤串.小饼和蘸料,音乐响起,啤酒倒满,烧烤灵魂的party即将开场的时候,你系统中的Scheduler(调试器),也自动根据设定的Trigger ...

  3. SpringBoot+Quartz动态管理定时任务

    前置理论: 1.小顶堆(适合任务少的,因为向下调整耗费性能) 堆:是一完全二叉树(除了最后一层节点其他层都达到最大节点数,且最后一层都靠左排列):堆中某个节点的值总不大于或不小于其父节点. 定时任务是 ...

  4. 定时任务动态管理-Scheduled

    文章目录 前言 一.架构流程图 二.代码实现流程 1.引入库 2.代码流程 前言 定时任务动态管理分为两种方式: 方式一:Web前台配置Trigger触发器(关联Cron).ThreadPoolTas ...

  5. SpringBoot 定时任务动态管理通用解决方案

    欢迎关注方志朋的博客,回复"666"获面试宝典 一.功能说明 SpringBoot的定时任务的加强工具,实现对SpringBoot原生的定时任务进行动态管理,完全兼容原生@Sche ...

  6. quart动态执行定时任务

    今天有个需求,前端可以将定时任务自定义保存到数据库,每天根据查询数据库来执行任务. 其实不用动态也是可以实现,但是.也是想试试动态执行定时任务看看怎么样的. (1)建立一个QuartzManage类 ...

  7. java定时执行sql语句_spring中使用quartz动态添加定时任务执行sql

    系统用来每天插入视图数据... 一.数据库表设计 1.接口配置表(t_m_db_interface_config) 2.接口日志表(t_m_db_interface_log) 3.前端配置页面 查询页 ...

  8. Celery 动态添加定时任务生产实践

    一.背景 实际工作中会有一些耗时的异步任务需要使用定时调度,比如发送邮件,拉取数据,执行定时脚本 通过celery 实现调度主要思想是 通过引入中间人redis,启动 worker 进行任务执行 ,c ...

  9. springboot整合Quartz实现动态配置定时任务

    版权声明:本文为博主原创文章,转载请注明出处. https://blog.csdn.net/liuchuanhong1/article/details/60873295 前言 在我们日常的开发中,很多 ...

最新文章

  1. c语言全局变量符号,C语言中的 @ 符号是什么意思?
  2. linux定时任务配置失效,linux下定时任务和延迟任务
  3. 数据中心的运维管理原则(一)
  4. 查询数据库游标使用情况以及sql
  5. 【转载】优酷网首席执行官兼创始人古永锵演讲
  6. [轉]数据挖掘工具的选择
  7. 覆盖所有面试知识点,持续更新中
  8. 微信小程序怎么弄成链接_自己怎么弄微信小程序?
  9. MFC工作笔记0003---WindowsAPI与MFC的关系
  10. webpack 4.0 中 clean-webpack-plugin 的使用
  11. 数据标准是物联网大集成应用的核心
  12. OSI七层模型:TCP/IP HTTP WebSocket MQTT
  13. rocketmq 双主双从同步写安装部署
  14. html静态网站基于游戏网站设计与实现共计10个页面 (仿地下城与勇士游戏网页)
  15. 网页在线验证工具(W3C国际标准验证)
  16. led大屏按实际尺寸设计画面_LED显示屏尺寸规格及计算方法
  17. PHP 毫秒级时间戳生成
  18. VS2017,MFC对WPS下Excel表格的操作
  19. Office Visio Project 2016下载地址
  20. 深度学习之卷积神经网络CNN 常用的几个模型

热门文章

  1. swiper ie11版本兼容调整
  2. 【停课不停学】CSDN学院奉绵薄之力,为程序员做点公益!
  3. JAVA面试题解惑系列(十)——话说多线程
  4. 首批!工信部下达2021年国家工业专项节能监察任务,涉及270 个数据中心(附名单)...
  5. 《Windows 8 权威指南》——2.8 Metro版IE10,探测Windows 8 Metro应用的撒手锏
  6. [转载]中国移动深度定制首推“0元购TD手机”补贴政策
  7. 有道云笔记如何修改全局默认字体样式
  8. 关于提词器的知识都在这了
  9. 字形码是计算机内部编码吗,汉字编码字形码
  10. 《用户至上:用户研究方法与实践(原书第2版)》一2.1 概述