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

您的位置:首頁技術(shù)文章
文章詳情頁

spring schedule配置多任務(wù)動態(tài)cron(增刪啟停)

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

之前公司經(jīng)常會遇到配置定時任務(wù),簡單的任務(wù)可以直接依賴spring。簡單任務(wù)直接使用 @scheduled 注解配合@EnableScheduling。但是如何實現(xiàn)簡單的動態(tài)cron呢?

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

倆種任務(wù)調(diào)度方式:

spring schedule配置多任務(wù)動態(tài)cron(增刪啟停)

二、本篇說明

springBoot 基礎(chǔ)模塊 spring-boot-starter-web 已經(jīng)內(nèi)置 schedule ,無需引入額外依賴。先思考幾個問題:

1、動態(tài) cron 實現(xiàn)的原理

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

ScheduledFuture SchedulingConfigurer ScheduledTaskRegistrar

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

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

4、如何配置任務(wù)分組根據(jù)自己業(yè)務(wù)背景,可根據(jù)步驟三,進行改造。

5、如何配置服務(wù)啟動自啟任務(wù)。想要程序啟動時首次去加我們設(shè)置的task,只需實現(xiàn) CommandLineRunner 即可。

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

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

參考類圖:

spring schedule配置多任務(wù)動態(tài)cron(增刪啟停)

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

三、代碼實現(xiàn)

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

普通多任務(wù)動態(tài)cron 分組多任務(wù)動態(tài)cron1. 普通多任務(wù)動態(tài)cron 實現(xiàn)

1.1 對應(yīng)數(shù)據(jù)庫的實體類 TaskEntity

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

1.2 配置每個任務(wù)實現(xiàn)

配置任務(wù)接口 TaskService

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

配置任務(wù)接口實現(xiàn) TaskServiceJob1Impl、TaskServiceJob2Impl …

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

1.3 配置任務(wù)解析器 TaskSolverChooser

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

@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 (動態(tài)cron核心配置)

說明:1、配置多線程執(zhí)行任務(wù)2、配置 刷新 task3、配置 停止 task4、配置 執(zhí)行task 業(yè)務(wù)邏輯

@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) { //設(shè)置20個線程,默認單線程,如果不設(shè)置的話,不能同時并發(fā)執(zhí)行任務(wù) registrar.setScheduler(Executors.newScheduledThreadPool(10)); this.registrar = registrar; } /** * 修改 cron 需要 調(diào)用該方法 */ public void refresh(List<TaskEntity> tasks){ //取消已經(jīng)刪除的策略任務(wù) 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(); //計劃任務(wù)表達式為空則跳過 if(!StringUtils.hasLength(expression)){continue; } //計劃任務(wù)已存在并且表達式未發(fā)生變化則跳過 if (scheduledFutures.containsKey(TaskEntity.getTaskId()) && cronTasks.get(TaskEntity.getTaskId()).getExpression().equals(expression)) {continue; } //如果策略執(zhí)行時間發(fā)生了變化,則取消當前策略的任務(wù) if(scheduledFutures.containsKey(TaskEntity.getTaskId())){scheduledFutures.get(TaskEntity.getTaskId()).cancel(false);scheduledFutures.remove(TaskEntity.getTaskId());cronTasks.remove(TaskEntity.getTaskId()); } //業(yè)務(wù)邏輯處理 CronTask task = cronTask(TaskEntity, expression); //執(zhí)行業(yè)務(wù) 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設(shè)成false話,不允許在線程運行時中斷,設(shè)成true的話就允許。scheduledFutures.get(item.getTaskId()).cancel(false);scheduledFutures.remove(item.getTaskId()); } }); } /** * 業(yè)務(wù)邏輯處理 */ public CronTask cronTask(TaskEntity TaskEntity, String expression) { return new CronTask(() -> { //每個計劃任務(wù)實際需要執(zhí)行的具體業(yè)務(wù)邏輯 //采用策略,模式 ,執(zhí)行我們的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 配置程序啟動時首次去加我們設(shè)置的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接口去觸發(fā),增刪啟停

@RestControllerpublic class StartController { @Autowired private MyScheduledTask scheduledTask; @PostMapping(value = '/startOrChangeCron') public String changeCron(@RequestBody List<TaskEntity> list){ if (CollectionUtils.isEmpty(list)) { // 這里模擬存在數(shù)據(jù)庫的數(shù)據(jù) list = Arrays.asList( new TaskEntity(1, '測試1','0/1 * * * * ?') , new TaskEntity(2, '測試2','0/1 * * * * ?') ); } scheduledTask.refresh(list); return 'task任務(wù):' + list.toString() + '已經(jīng)開始運行'; } @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任務(wù):' + collect.toString() + '已經(jīng)停止啟動'; }}2. 分組多任務(wù)動態(tài)cron 實現(xiàn)

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

3 測試記錄

測試1 項目啟動自啟TaskServiceJob1Impl和TaskServiceJob1Impl … 設(shè)置 阻塞10s觀察日志時間可發(fā)現(xiàn),已經(jīng)同時并發(fā)執(zhí)行倆個任務(wù)。

spring schedule配置多任務(wù)動態(tài)cron(增刪啟停)

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

spring schedule配置多任務(wù)動態(tài)cron(增刪啟停)

觀察日志

spring schedule配置多任務(wù)動態(tài)cron(增刪啟停)

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

四、總結(jié)

其實實現(xiàn)簡單的動態(tài)配置,以上代碼可用,比較簡單。

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

標簽: Spring
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
久久夜色精品| 91国内精品| 国产精品调教视频| 亚洲男女av一区二区| 青青久久av| 亚洲1234区| 亚洲激情国产| 亚洲精品三级| 欧美交a欧美精品喷水| 麻豆精品视频在线观看免费| 国产日韩欧美在线播放不卡| 国产精品日本一区二区三区在线| 国产精品一区二区av交换 | 欧美另类中文字幕| 国产精品久久久一区二区| 国产精品v日韩精品v欧美精品网站| 亚洲精品日韩久久| 欧美伊人影院| 97在线精品| 日韩欧美一区免费| 日本a级不卡| 91亚洲国产高清| 伊人精品一区| 91成人小视频| 国产一区二区三区天码| 亚洲va在线| 欧美一级一区| 久久天堂成人| 国产亚洲一区| 久久久久91| 日韩av中文字幕一区| 视频一区二区国产| 日本综合精品一区| 久久电影tv| 日韩欧美中文字幕电影 | 激情国产在线| 日韩欧美激情| 国产一区白浆| 欧洲av不卡| 久久av资源| 亚洲婷婷丁香| 国产综合视频| 国产一区二区三区精品在线观看| 巨乳诱惑日韩免费av| 麻豆91精品91久久久的内涵| 日韩中文字幕91| 欧美亚洲在线日韩| 成人污污视频| 国产乱码精品一区二区亚洲| 影音国产精品| jiujiure精品视频播放| 国产一区二区亚洲| 久久99蜜桃| 亚洲精品无播放器在线播放| 亚洲国内欧美| 亚洲成人国产| 荡女精品导航| 久久不卡国产精品一区二区| 99国产精品久久久久久久| 精品香蕉视频| 日本欧美韩国一区三区| 性欧美69xoxoxoxo| 久久一区二区三区喷水| 精精国产xxxx视频在线播放| 成人一二三区| 久久91导航| 激情综合自拍| 午夜日韩av| 亚洲一区欧美二区| 91久久亚洲| 欧美专区在线| 综合激情网站| 丝袜国产日韩另类美女| 男女男精品网站| 在线一区视频| 水野朝阳av一区二区三区| 蜜桃伊人久久| 日韩成人精品一区二区三区 | 欧美激情99| 欧美激情麻豆| 久久精品国产久精国产爱| 国产精品红桃| 高清精品久久| 国产综合精品一区| 亚洲精品黄色| 日韩一区二区三区精品视频第3页| 日本成人在线不卡视频| 欧美极品中文字幕| 亚洲午夜黄色| 91精品国产自产精品男人的天堂| 精品资源在线| 亚洲美洲欧洲综合国产一区| 伊人国产精品| 高清在线一区| 亚洲久久在线| 亚洲美女久久精品| 亚洲一区二区免费看| 麻豆成人综合网| 国产精品毛片在线| 久久影视三级福利片| 成人av三级| 亚洲精品第一| 久久国产电影| 麻豆国产精品| 少妇精品久久久一区二区| 国产一区二区三区久久| 亚欧洲精品视频在线观看| 超碰99在线| 国产日韩视频在线| 日韩精品一二三区| 免费观看不卡av| 成人一区而且| 另类专区亚洲| 亚洲综合丁香| 日韩av片子| 日韩精品欧美激情一区二区| 男女男精品网站| 五月婷婷六月综合| 日韩不卡免费高清视频| 免费看久久久| 国产精品夜夜夜| 日韩综合一区二区| 久久国产精品毛片| 国产精品社区| 野花国产精品入口| 成人羞羞视频在线看网址| 麻豆精品一区二区综合av| 青草久久视频| 欧美一级二级视频| 日韩欧美中文字幕一区二区三区| 日韩视频二区| 欧美日韩精品一本二本三本| 久久视频精品| 亚洲福利免费| 国产99精品| 狠狠久久婷婷| 久久高清国产| 日韩高清电影免费| 久久国产免费看| 久久中文欧美| 亚洲va中文在线播放免费| 久久亚洲精品中文字幕蜜潮电影| 国产精品久久久久毛片大屁完整版| 91精品高清| 日韩手机在线| 国产精品网在线观看| 国产精品yjizz视频网| 久久精品免费一区二区三区| 91久久久精品国产| 亚洲精品麻豆| 国产精品二区不卡| 亚洲中字黄色| 你懂的亚洲视频| 久久精品91| 亚洲精品欧美| 日产精品一区| 日韩欧美精品一区二区综合视频| 久久中文欧美| 久久亚洲二区| 色一区二区三区四区| 国模 一区 二区 三区| 日本成人精品| 欧美日中文字幕| 国产精品一区2区3区| 激情五月综合| 久久av导航| 亚洲综合小说| 日韩精品水蜜桃| 国产日产精品_国产精品毛片| 欧美成人a交片免费看| 人人精品久久| 久久aⅴ国产紧身牛仔裤| 精品91福利视频| 日韩精品亚洲aⅴ在线影院| 午夜精品成人av| 国产精选久久| 亚洲第一精品影视| 成人三级高清视频在线看| 亚洲乱码视频| 久久国产电影| 91视频精品| 国产一区二区三区久久| 国产精品亚洲综合久久| 亚洲色图综合| 伊人久久大香线蕉av不卡| 国产精品一区二区99| 日韩综合一区二区| 亚洲欧美日本国产| 伊人影院久久| 日本欧美不卡| 日韩免费一区| 欧美片第1页综合| 在线视频亚洲欧美中文| 欧美日韩国产免费观看| 日韩欧美午夜| 一本一本久久| 免费在线观看精品| 一区二区三区国产在线| 欧美日韩国产精品一区二区亚洲| 色吊丝一区二区|