日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

spring schedule配置多任務動態cron(增刪啟停)

瀏覽:30日期:2023-07-19 16:55:24
一、背景

之前公司經常會遇到配置定時任務,簡單的任務可以直接依賴spring。簡單任務直接使用 @scheduled 注解配合@EnableScheduling。但是如何實現簡單的動態cron呢?

開發原則:盡可能在項目本身去實現,少依賴第三方框架,避免項目過于臃腫和復雜。

倆種任務調度方式:

spring schedule配置多任務動態cron(增刪啟停)

二、本篇說明

springBoot 基礎模塊 spring-boot-starter-web 已經內置 schedule ,無需引入額外依賴。先思考幾個問題:

1、動態 cron 實現的原理

任務的 【 停止】是基于 future接口 的cancel() 方法。任務的 【增加、刪除、啟動】是基于 注冊到 類ScheduledTaskRegistrar 的 ScheduledFuture的數量。涉及核心類:

ScheduledFuture SchedulingConfigurer ScheduledTaskRegistrar

2、多任務并行執行配置spring默認機制對schedule是單線程,需要配置多線程并行執行。

3、如何配置多個任務好多博文,都是配置一個cron,這讓初學者很難受。

4、如何配置任務分組根據自己業務背景,可根據步驟三,進行改造。

5、如何配置服務啟動自啟任務。想要程序啟動時首次去加我們設置的task,只需實現 CommandLineRunner 即可。

6、如何從數據庫讀取配置這個其實很簡單,在實現 ScheduledTaskRegistrar 時,先直接查詢我們需要的數據即可。

7、如何優雅的實現我們的代碼這里為了我們多個task實現時,去除臃腫的if else ,使用策略模式去實現我們的task,這里代碼里面會具體介紹。

參考類圖:

spring schedule配置多任務動態cron(增刪啟停)

8、如何去觸發我們的schedule 【增刪啟停】配置好 task任務類,注入到 controller ,通過接口直接調用即可。

三、代碼實現

先貼出我的github 代碼,下面代碼描述不全。

普通多任務動態cron 分組多任務動態cron1. 普通多任務動態cron 實現

1.1 對應數據庫的實體類 TaskEntity

@Data@AllArgsConstructor@NoArgsConstructorpublic class TaskEntity { /** * 任務id */ private int taskId; /** * 任務說明 */ private String desc; /** * cron 表達式 */ private String expression;}

1.2 配置每個任務實現

配置任務接口 TaskService

public interface TaskService { void HandlerJob(); Integer jobId();}

配置任務接口實現 TaskServiceJob1Impl、TaskServiceJob2Impl …

@Servicepublic class TaskServiceJob1Impl implements TaskService { @Override public void HandlerJob() { System.out.println('------job1 開始執行---------:'+new Date()); System.out.println(new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').format(new Date()) + ' ' + Thread.currentThread().getName() + ' 任務一啟動'); try { Thread.sleep(10000);//任務耗時10秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(new SimpleDateFormat('yyyy-MM-dd HH:mm:ss').format(new Date()) + ' ' + Thread.currentThread().getName() + ' 結束'); } @Override public Integer jobId() { return 1; }}

1.3 配置任務解析器 TaskSolverChooser

注:這里引入策略模式為啥要配置 任務解析器選擇器:因為我們實現多個任務時,一個任務對應一個 CronTask,需要在 MyScheduledTask 里面去實現我們每一個方法。譬如,我們有100個任務就要自定義100個任務實現方法,代碼會很臃腫,明顯不符合,【開閉原則】,于是這里采用策略模式,解耦我們多個任務業務實現邏輯。

@Slf4j@Componentpublic class TaskSolverChooser implements ApplicationContextAware { private ApplicationContext applicationContext; private Map<Integer, TaskService> chooseMap = new HashMap<>(16); /** * 拿到spring context 上下文 */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @PostConstruct private void registerToTaskSolver(){ Map<String, TaskService> taskServiceMap = applicationContext.getBeansOfType(TaskService.class); for (TaskService value : taskServiceMap.values()) { chooseMap.put(value.jobId(), value); log.info('task {} 處理器: {} 注冊成功',new Object[]{value.jobId(),value}); } } /** * 獲取需要的job */ public TaskService getTask(Integer jobId){ return chooseMap.get(jobId); }}

1.4 配置MyScheduledTask (動態cron核心配置)

說明:1、配置多線程執行任務2、配置 刷新 task3、配置 停止 task4、配置 執行task 業務邏輯

@Componentpublic class MyScheduledTask implements SchedulingConfigurer { private volatile ScheduledTaskRegistrar registrar; private final ConcurrentHashMap<Integer, ScheduledFuture<?>> scheduledFutures = new ConcurrentHashMap<>(); private final ConcurrentHashMap<Integer, CronTask> cronTasks = new ConcurrentHashMap<>(); @Autowired private TaskSolverChooser taskSolverChooser; @Override public void configureTasks(ScheduledTaskRegistrar registrar) { //設置20個線程,默認單線程,如果不設置的話,不能同時并發執行任務 registrar.setScheduler(Executors.newScheduledThreadPool(10)); this.registrar = registrar; } /** * 修改 cron 需要 調用該方法 */ public void refresh(List<TaskEntity> tasks){ //取消已經刪除的策略任務 Set<Integer> sids = scheduledFutures.keySet(); for (Integer sid : sids) { if(!exists(tasks, sid)){scheduledFutures.get(sid).cancel(false); } } for (TaskEntity TaskEntity : tasks) { String expression = TaskEntity.getExpression(); //計劃任務表達式為空則跳過 if(!StringUtils.hasLength(expression)){continue; } //計劃任務已存在并且表達式未發生變化則跳過 if (scheduledFutures.containsKey(TaskEntity.getTaskId()) && cronTasks.get(TaskEntity.getTaskId()).getExpression().equals(expression)) {continue; } //如果策略執行時間發生了變化,則取消當前策略的任務 if(scheduledFutures.containsKey(TaskEntity.getTaskId())){scheduledFutures.get(TaskEntity.getTaskId()).cancel(false);scheduledFutures.remove(TaskEntity.getTaskId());cronTasks.remove(TaskEntity.getTaskId()); } //業務邏輯處理 CronTask task = cronTask(TaskEntity, expression); //執行業務 ScheduledFuture<?> future = registrar.getScheduler().schedule(task.getRunnable(), task.getTrigger()); cronTasks.put(TaskEntity.getTaskId(), task); scheduledFutures.put(TaskEntity.getTaskId(), future); } } /** * 停止 cron 運行 */ public void stop(List<TaskEntity> tasks){ tasks.forEach(item->{ if (scheduledFutures.containsKey(item.getTaskId())) {// mayInterruptIfRunning設成false話,不允許在線程運行時中斷,設成true的話就允許。scheduledFutures.get(item.getTaskId()).cancel(false);scheduledFutures.remove(item.getTaskId()); } }); } /** * 業務邏輯處理 */ public CronTask cronTask(TaskEntity TaskEntity, String expression) { return new CronTask(() -> { //每個計劃任務實際需要執行的具體業務邏輯 //采用策略,模式 ,執行我們的job taskSolverChooser.getTask(TaskEntity.getTaskId()).HandlerJob();}, expression); } private boolean exists(List<TaskEntity> tasks, Integer tid){ for(TaskEntity TaskEntity:tasks){ if(TaskEntity.getTaskId() == tid){return true; } } return false; } @PreDestroy public void destroy() { this.registrar.destroy(); }}

1.5 配置程序啟動時首次去加我們設置的task

@Componentpublic class StartInitTask implements CommandLineRunner { @Autowired private MyScheduledTask myScheduledTask; @Override public void run(String... args) throws Exception { List<TaskEntity> list = Arrays.asList(new TaskEntity(1, '測試1', '0/1 * * * * ?'),new TaskEntity(2, '測試2', '0/1 * * * * ?') ); myScheduledTask.refresh(list); }}

1.6 配置web接口去觸發,增刪啟停

@RestControllerpublic class StartController { @Autowired private MyScheduledTask scheduledTask; @PostMapping(value = '/startOrChangeCron') public String changeCron(@RequestBody List<TaskEntity> list){ if (CollectionUtils.isEmpty(list)) { // 這里模擬存在數據庫的數據 list = Arrays.asList( new TaskEntity(1, '測試1','0/1 * * * * ?') , new TaskEntity(2, '測試2','0/1 * * * * ?') ); } scheduledTask.refresh(list); return 'task任務:' + list.toString() + '已經開始運行'; } @PostMapping(value = '/stopCron') public String stopCron(@RequestBody List<TaskEntity> list){ if (CollectionUtils.isEmpty(list)) { // 這里模擬將要停止的cron可通過前端傳來 list = Arrays.asList( new TaskEntity(1, '測試1','0/1 * * * * ?') , new TaskEntity(2, '測試2','0/1 * * * * ?') ); } scheduledTask.stop(list); List<Integer> collect = list.stream().map(TaskEntity::getTaskId).collect(Collectors.toList()); return 'task任務:' + collect.toString() + '已經停止啟動'; }}2. 分組多任務動態cron 實現

實現原理:基于反射實現,根據方法全類名,去動態執行方法。多任務分組配置,根據任務類型進行分組。eg:定時任務人員的相關操作,有檢測人員離職狀態,人員業績達標,人員考勤…等,作用:對人員定時任務做一個分類,在同一個類里面去實現不同的task,比較《1. 普通多任務動態cron 實現》,是一個類可以實現一個task《2. 分組多任務動態cron 實現》,是一個類可以實現多個task詳細可參考: 分組多任務動態cron

3 測試記錄

測試1 項目啟動自啟TaskServiceJob1Impl和TaskServiceJob1Impl … 設置 阻塞10s觀察日志時間可發現,已經同時并發執行倆個任務。

spring schedule配置多任務動態cron(增刪啟停)

測試2 觸發 刷新【增、刪、啟】我們的task,。其實這里沒這么智能,如果需要觸發刷新接口,實際上是重新加載我們的task,就是對應觸發我們,增加任務任務,刪除任務,啟動任務。使用idea插件測試接口

spring schedule配置多任務動態cron(增刪啟停)

觀察日志

spring schedule配置多任務動態cron(增刪啟停)

測試3 觸發 停止接口,停止一個接口。這里測試略過…

四、總結

其實實現簡單的動態配置,以上代碼可用,比較簡單。

到此這篇關于spring schedule配置多任務動態cron(增刪啟停)的文章就介紹到這了,更多相關spring schedule 多任務動態cron內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产精品一区三区在线观看| 伊人影院久久| 欧美日韩xxxx| 欧美天堂一区| 日韩激情中文字幕| 国产精品欧美在线观看| 国产精品极品在线观看| 欧美日韩国产一区二区在线观看| 久久国产精品免费精品3p| 国产精品玖玖玖在线资源| 精品五月天堂| 久久精品导航| 亚洲少妇在线| 青青草精品视频| 丁香婷婷久久| 亚洲福利一区| 亚洲日韩视频| 免费亚洲婷婷| 九九色在线视频| 久久av在线| 国产图片一区| 日韩黄色大片| 99日韩精品| 欧美日韩va| 国产成人久久精品一区二区三区| 久久精品高清| 日韩欧美中文在线观看| 国产精品一区二区免费福利视频| 精品国产乱码久久久久久樱花 | 久久福利影视| 亚洲精品影视| 激情不卡一区二区三区视频在线| 日韩成人亚洲| 日韩精品一区二区三区中文在线| 老色鬼精品视频在线观看播放| 免费福利视频一区二区三区| 激情欧美一区二区三区| 亚洲精品大片| 日韩在线观看一区| 亚洲一区二区av| 精品久久亚洲| 爽爽淫人综合网网站| 国产精品黄色片| 国产一区二区三区自拍| 91免费精品国偷自产在线在线| аⅴ资源天堂资源库在线| 亚洲神马久久| 电影91久久久| 亚洲一级大片| 快播电影网址老女人久久| 美女黄网久久| 韩国女主播一区二区三区| 五月综合激情| 国产欧美日韩视频在线| 久久伦理在线| 国产精品手机在线播放| 偷拍欧美精品| 国产一区二区三区91| 玖玖玖国产精品| 成人国产精品一区二区免费麻豆| 免费精品视频在线| 亚洲www免费| 日韩av资源网| 亚洲福利精品| 国产一区二区三区四区大秀| 久久先锋影音| 日韩毛片视频| 久久国内精品自在自线400部| 亚洲高清二区| 精品一区二区三区免费看 | 欧美日韩一区二区三区不卡视频| 久久久久中文| 欧美交a欧美精品喷水| 国产精品日韩| 久久久久99| 久久精品国产成人一区二区三区| 综合国产视频| 国产亚洲精品久久久久婷婷瑜伽| 欧美激情视频一区二区三区在线播放| 中文字幕中文字幕精品| 激情欧美日韩一区| 国产精品yjizz视频网| 国产精品永久| 日韩国产在线一| 美女精品在线观看| 午夜免费一区| 久久蜜桃精品| 97国产精品| 精品国产欧美日韩一区二区三区| 欧美日韩精品一区二区三区在线观看| 一本色道精品久久一区二区三区| 日韩在线欧美| av中文资源在线资源免费观看| 日韩av成人高清| 亚洲欧洲专区| 蜜桃久久久久久久| 久久午夜影视| 日韩一区欧美二区| 麻豆成人在线| 性色一区二区| 美女久久网站| 香蕉久久国产| 亚洲综合国产| 久久性天堂网| 日av在线不卡| 综合激情婷婷| 美美哒免费高清在线观看视频一区二区| 午夜欧美精品| 美女亚洲一区| 红桃视频亚洲| 夜夜嗨一区二区三区| 9国产精品视频| 免费欧美日韩| 亚洲香蕉视频| 少妇精品久久久一区二区| 亚州精品视频| 欧美亚洲三级| 国产精品xxx| 成人污污视频| 香蕉视频亚洲一级| 欧美成人综合| 丝袜脚交一区二区| 日韩一区精品字幕| 蜜桃视频一区二区三区| 日韩中文字幕一区二区高清99| 日韩有码av| 欧美一区二区三区免费看| 青青草91久久久久久久久| 欧美日韩91| 欧美精品1区| 国产精品成人一区二区不卡| 欧美日韩国产观看视频| 久久国产电影| 国产亚洲福利| 亚洲精品无播放器在线播放| 日产欧产美韩系列久久99| 欧美一区成人| 久久精品三级| 高清av一区| 天堂资源在线亚洲| 久久先锋影音| 日韩不卡在线观看日韩不卡视频| 国产精品色在线网站| 日本久久综合| 午夜国产欧美理论在线播放 | 国产日产高清欧美一区二区三区| 国产精品a级| www在线观看黄色| 精品中文字幕一区二区三区av| 亚洲专区在线| 热久久久久久| 精品三级在线观看视频| 欧美天堂视频| 国产精品丝袜xxxxxxx| 日韩精品三级| 精品亚洲a∨一区二区三区18| 日韩欧美1区| 亚洲国产日韩欧美在线| 日本不卡一区二区三区| 国产成人精选| 久久xxxx| 精品国产亚洲一区二区三区在线 | 黑森林国产精品av| 宅男噜噜噜66国产日韩在线观看| 婷婷综合福利| 成人片免费看| 蜜桃一区二区三区在线| 麻豆一区在线| 亚洲午夜av| 日本h片久久| 电影天堂国产精品| 中文字幕一区二区三区在线视频| 国产极品嫩模在线观看91精品| 亚洲人成在线网站| 中文字幕一区日韩精品| 国产一区国产二区国产三区| 日韩一区二区免费看| 日韩精选在线| 亚洲伦乱视频| 日韩av中文字幕一区二区三区| 日韩国产欧美| 欧美精品影院| 欧美亚洲激情| 国产免费播放一区二区| 精品中文一区| 久久永久免费| 亚洲日本三级| 91精品国产调教在线观看| 日本成人在线不卡视频| 久久精品主播| 国产精品一卡| 男人的天堂久久精品| 日韩不卡一区| 日韩国产欧美三级| 亚洲二区精品| 欧美丰满日韩| 国产欧美精品| 亚洲欧美日韩国产一区| 中文av在线全新| 欧美一级网址|