基于Spring實現(xiàn)零重啟自由編排任務(wù)的定時管理器
大家好,我是小趴菜,關(guān)于定時任務(wù)相信大家在項目中使用了很多,我們一般都是使用Spring自帶的@EnableScheduling來實現(xiàn)定時任務(wù)
雖然Spring自帶的定時任務(wù)已經(jīng)可以滿足我們的業(yè)務(wù)需求,但是它還是有不足的地方
- 我們需要改變定時任務(wù)的時間,此時我們就需要重啟項目
- 我們不需要這個定時任務(wù)了,我們就要修改代碼,然后重啟項目
- 后期我們又要開啟這個定時任務(wù),我們又要修改代碼,然后重啟項目
我們發(fā)現(xiàn),我們使用Spring自帶的定時任務(wù)如果要有修改,那么就要修改代碼,然后重啟項目,那么有沒有辦法能夠讓我們不重啟項目就可以直接編排我們自己的定時任務(wù)呢?
答案是:有的,接下來我就帶大家實現(xiàn)一個零重啟自由編排任務(wù)的定時管理器
實現(xiàn)
創(chuàng)建一個普通的SpringBoot項目,首先我們需要一個配置類,來配置定時任務(wù)的線程池
package com.coco.schedule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @Configuration public class ScheduleConfig { @Bean public ThreadPoolTaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); //定制化我們的線程池 taskScheduler.setPoolSize(1); taskScheduler.setThreadNamePrefix("myTask-"); return taskScheduler; } }
創(chuàng)建一個接口,以供其它服務(wù)調(diào)用
/** * 定時任務(wù)的任務(wù)接口,需要實現(xiàn)Runnable接口 */ public interface ScheduleTask extends Runnable { /** * 獲取定時任務(wù)的名稱 */ String getTaskName(); }
接下來就是定時任務(wù)的編排核心處理類
package com.coco.schedule; import cn.hutool.core.util.StrUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.support.CronTrigger; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; /** * 定時任務(wù)管理器 */ @Component public class ScheduleManager { private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleManager.class); @Resource private ThreadPoolTaskScheduler taskScheduler; /** * 內(nèi)部正在執(zhí)行的定時任務(wù)緩存 */ private Map<String,ScheduleTaskHolder> cache = new ConcurrentHashMap<>(); /** * 啟動一個定時任務(wù) * scheduleTask:定時任務(wù)實現(xiàn)類 * cron:cron表達式 */ public String startTask(ScheduleTask scheduleTask,String cron) { ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(scheduleTask, new CronTrigger(cron)); //使用UUID生成定時任務(wù)的唯一key String key = UUID.randomUUID().toString(); //將定時任務(wù)與定時任務(wù)結(jié)果封裝成ScheduleTaskHolder對象, //這個對象下文有源碼,也是我們自定義的 ScheduleTaskHolder scheduleTaskHolder = new ScheduleTaskHolder(scheduleTask,scheduledFuture); //將正在執(zhí)行的定時任務(wù)緩存起來 cache.put(key,scheduleTaskHolder); LOGGER.info("定時任務(wù)啟動成功,key = {}",key); return key; } /** * 停止一個定時任務(wù) * @param key:緩存里面的定時任務(wù)的key */ public void stopTask(String key) { //基本判斷 if(StrUtil.isBlank(key) || !cache.containsKey(key)) { return; } //從緩存中拿到這個定時任務(wù) ScheduleTaskHolder scheduleTaskHolder = cache.get(key); if(scheduleTaskHolder == null) { return; } ScheduledFuture scheduledFuture = scheduleTaskHolder.getScheduledFuture(); //停止這個定時任務(wù) boolean isCancel = scheduledFuture.cancel(true); if(isCancel) { //停止成功,就從緩存中移除這個定時任務(wù) cache.remove(key); LOGGER.info("定時任務(wù)停止成功,key = {}",key); }else { LOGGER.error("定時任務(wù)停止失敗,key = {}",key); } } /** * 停止一個定時任務(wù) * @param key:緩存里面的定時任務(wù)的key * @param cron:新的cron表達式 */ public String changeTask(String key,String cron) { //基本判空處理 if(StrUtil.isBlank(key) || StrUtil.isBlank(cron)) { throw new RuntimeException("key and cron mast not null"); } ScheduleTaskHolder scheduleTaskHolder = cache.get(key); if(scheduleTaskHolder == null) { throw new RuntimeException("ScheduleTaskHolder not exist,key = {}" + key); } //先停止這個定時任務(wù) stopTask(key); //然后重啟開啟一個定時任務(wù) return startTask(scheduleTaskHolder.getScheduleTask(),cron); } }
定時任務(wù)與定時任務(wù)結(jié)果的緩存封裝類
package com.coco.schedule; import cn.hutool.core.util.StrUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.support.CronTrigger; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; /** * 定時任務(wù)管理器 */ @Component public class ScheduleManager { private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleManager.class); @Resource private ThreadPoolTaskScheduler taskScheduler; /** * 內(nèi)部正在執(zhí)行的定時任務(wù)緩存 */ private Map<String,ScheduleTaskHolder> cache = new ConcurrentHashMap<>(); /** * 啟動一個定時任務(wù) * scheduleTask:定時任務(wù)實現(xiàn)類 * cron:cron表達式 */ public String startTask(ScheduleTask scheduleTask,String cron) { ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(scheduleTask, new CronTrigger(cron)); //使用UUID生成定時任務(wù)的唯一key String key = UUID.randomUUID().toString(); //將定時任務(wù)與定時任務(wù)結(jié)果封裝成ScheduleTaskHolder對象, //這個對象下文有源碼,也是我們自定義的 ScheduleTaskHolder scheduleTaskHolder = new ScheduleTaskHolder(scheduleTask,scheduledFuture); //將正在執(zhí)行的定時任務(wù)緩存起來 cache.put(key,scheduleTaskHolder); LOGGER.info("定時任務(wù)啟動成功,key = {}",key); return key; } /** * 停止一個定時任務(wù) * @param key:緩存里面的定時任務(wù)的key */ public void stopTask(String key) { //基本判斷 if(StrUtil.isBlank(key) || !cache.containsKey(key)) { return; } //從緩存中拿到這個定時任務(wù) ScheduleTaskHolder scheduleTaskHolder = cache.get(key); if(scheduleTaskHolder == null) { return; } ScheduledFuture scheduledFuture = scheduleTaskHolder.getScheduledFuture(); //停止這個定時任務(wù) boolean isCancel = scheduledFuture.cancel(true); if(isCancel) { //停止成功,就從緩存中移除這個定時任務(wù) cache.remove(key); LOGGER.info("定時任務(wù)停止成功,key = {}",key); }else { LOGGER.error("定時任務(wù)停止失敗,key = {}",key); } } /** * 停止一個定時任務(wù) * @param key:緩存里面的定時任務(wù)的key * @param cron:新的cron表達式 */ public String changeTask(String key,String cron) { //基本判空處理 if(StrUtil.isBlank(key) || StrUtil.isBlank(cron)) { throw new RuntimeException("key and cron mast not null"); } ScheduleTaskHolder scheduleTaskHolder = cache.get(key); if(scheduleTaskHolder == null) { throw new RuntimeException("ScheduleTaskHolder not exist,key = {}" + key); } //先停止這個定時任務(wù) stopTask(key); //然后重啟開啟一個定時任務(wù) return startTask(scheduleTaskHolder.getScheduleTask(),cron); } }
測試
寫好的程序怎么能不測試,首先我們需要創(chuàng)建一個自己的定時任務(wù)執(zhí)行的業(yè)務(wù)處理類,我這里創(chuàng)建了個MyTask.class
package com.coco.schedule; import org.springframework.stereotype.Component; //需要實現(xiàn)ScheduleTask接口 @Component public class MyTask implements ScheduleTask { @Override public String getTaskName() { return "MyTask"; } @Override public void run() { //在這里就是我們定時任務(wù)的業(yè)務(wù)邏輯 System.out.println("這是自定義的定時起任務(wù)"); } }
為了簡單,我直接在啟動類中編寫接口了,大家別跟我一樣這么懶哈
package com.coco; import com.coco.schedule.MyTask; import com.coco.schedule.ScheduleManager; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class,args); } @Resource private ScheduleManager scheduleManager; @Resource private MyTask myTask; //啟動我們的定時任務(wù) @GetMapping("/startTask") public String startTask() { String key = scheduleManager.startTask(myTask, "0/10 * * * * ? "); return key; } //修改我們的定時任務(wù) @GetMapping("/changeTask") public String changeTask(@RequestParam("key") String key) { String keyValue = scheduleManager.changeTask(key, "0/20 * * * * ? "); return keyValue; } //停止我們的定時任務(wù) @GetMapping("/stopTask") public String stopTask(@RequestParam("key") String key) { scheduleManager.stopTask(key); return "ok"; } }
可以先啟動,然后修改,最后停止
到此這篇關(guān)于基于Spring實現(xiàn)零重啟自由編排任務(wù)的定時管理器的文章就介紹到這了,更多相關(guān)Spring定時管理器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot多環(huán)境開發(fā)及使用方法
這篇文章主要介紹了Springboot多環(huán)境開發(fā)及多環(huán)境設(shè)置使用、多環(huán)境分組管理的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03Springboot整合JPA配置多數(shù)據(jù)源流程詳解
這篇文章主要介紹了Springboot整合JPA配置多數(shù)據(jù)源,JPA可以通過實體類生成數(shù)據(jù)庫的表,同時自帶很多增刪改查方法,大部分sql語句不需要我們自己寫,配置完成后直接調(diào)用方法即可,很方便2022-11-11java8?Stream大數(shù)據(jù)量List分批處理切割方式
這篇文章主要介紹了java8?Stream大數(shù)據(jù)量List分批處理切割方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02Spring Boot中單例類實現(xiàn)對象的注入方式
這篇文章主要介紹了Spring Boot中單例類實現(xiàn)對象的注入方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08基于SpringBoot和Vue3的博客平臺文章詳情與評論功能實現(xiàn)
在前面的教程中,我們已經(jīng)實現(xiàn)了基于Spring Boot和Vue3的發(fā)布、編輯、刪除文章功能以及文章列表與分頁功能。本教程將引導您實現(xiàn)博客平臺的文章詳情與評論功能,需要的朋友可以參考一下2023-04-04