電子商務(wù)網(wǎng)站建設(shè)個(gè)人總結(jié)推廣學(xué)院seo教程
前段時(shí)間編寫了一篇博客SpringBoot 動(dòng)態(tài)操作定時(shí)任務(wù)(啟動(dòng)、停止、修改執(zhí)行周期,該篇博客還是幫助了很多同學(xué)。
但是該篇博客中的方法有些不足的地方:
- 只能通過(guò)前端控制器controller手動(dòng)注冊(cè)任務(wù)?!揪唧w的應(yīng)該是我們提前配置好我們的任務(wù),配置完成后讓springboot應(yīng)用幫我們加載并注冊(cè)任務(wù)】
- 無(wú)法打印任務(wù)的啟動(dòng)時(shí)間、下次執(zhí)行時(shí)間及任務(wù)耗時(shí)等情況。
所以針對(duì)以上的不足我對(duì)該方案進(jìn)行了整改。
新方案涉及4個(gè)類:
- TaskSchedulerConfig 任務(wù)調(diào)度器及任務(wù)注冊(cè)器的配置
- ScheduledTaskRegistrar 任務(wù)注冊(cè)器,用于將定時(shí)任務(wù)注冊(cè)到調(diào)度器中
- ScheduledTaskHolder 任務(wù)的包裝類
- ITask 任務(wù)抽象類
提醒:該定時(shí)任務(wù)只能實(shí)現(xiàn)單機(jī)環(huán)境下使用,無(wú)法在分布式環(huán)境下使用。
一、核心實(shí)現(xiàn)如下:
1、配置類TaskSchedulerConfig
該配置類往spring容器中注冊(cè)了兩個(gè)bean,第一個(gè)bean為org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler,該bean為spring提供的基于線程池的任務(wù)調(diào)度器,其本質(zhì)是持有一個(gè)java.util.concurrent.ScheduledThreadPoolExecutor,這里需要注意初始化ScheduledThreadPoolExecutor的時(shí)候最大線程數(shù)為Integer.MAX_VALU。
setRemoveOnCancelPolicy方法如果設(shè)置為true,則在中斷當(dāng)前執(zhí)行的任務(wù)后會(huì)將其從任務(wù)等待隊(duì)列(如果隊(duì)列中有該任務(wù))中移除。
第二個(gè)bean為ScheduledTaskRegistrar即我們的任務(wù)注冊(cè)器,該bean需要持有一個(gè)任務(wù)調(diào)度器并且需要配置任務(wù)列表【目前任務(wù)列表需要手動(dòng)配置如果同學(xué)們想增強(qiáng)的話可以自己實(shí)現(xiàn)注解掃描配置或者包掃描配置】。
package com.bbs.config.scheduled;import com.bbs.task.MoniterTask;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;import java.util.Arrays;/*** @Author whh* @Date: 2023/03/15/ 20:15* @description*/
@Configuration
public class TaskSchedulerConfig {/**** 本質(zhì)是ScheduledThreadPoolExecutor的包裝,其中初始化ScheduledThreadPoolExecutor的構(gòu)造函數(shù)如下* 特別的要注意最大線程數(shù)為 Integer.MAX_VALUE** public ScheduledThreadPoolExecutor(int corePoolSize,* ThreadFactory threadFactory,* RejectedExecutionHandler handler) {* super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,* new DelayedWorkQueue(), threadFactory, handler);* }* @return*/@Beanpublic ThreadPoolTaskScheduler threadPoolTaskScheduler(){ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();threadPoolTaskScheduler.setPoolSize(3);threadPoolTaskScheduler.setRemoveOnCancelPolicy(true);return threadPoolTaskScheduler;}@Beanpublic ScheduledTaskRegistrar taskRegistrar(ThreadPoolTaskScheduler scheduler){ScheduledTaskRegistrar taskRegistrar = new ScheduledTaskRegistrar(scheduler);MoniterTask moniterTask = new MoniterTask("*/30 * * * * ?");moniterTask.setTaskName("監(jiān)控任務(wù)");moniterTask.setTaskDescription("每隔30s對(duì)機(jī)器進(jìn)行監(jiān)控");taskRegistrar.setTaskes(Arrays.asList(moniterTask));return taskRegistrar;}
}
2、任務(wù)注冊(cè)器ScheduledTaskRegistrar
該任務(wù)注冊(cè)器實(shí)現(xiàn)了org.springframework.beans.factory.InitializingBean接口,所以可以達(dá)到配置完成后讓springboot應(yīng)用幫我們加載并注冊(cè)任務(wù)。該bean主要有5個(gè)方法,包括注冊(cè)任務(wù)、查詢所有任務(wù)、立即執(zhí)行任務(wù)、暫停任務(wù)、任務(wù)恢復(fù)。其中立即執(zhí)行任務(wù)并未使用調(diào)度器的線程執(zhí)行而是使用用戶線程執(zhí)行。
package com.bbs.config.scheduled;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.StringUtils;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;/*** @Author whh* @Date: 2023/03/15/ 19:44* @description 任務(wù)注冊(cè)*/
public class ScheduledTaskRegistrar implements InitializingBean {private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTaskRegistrar.class);/*** 任務(wù)調(diào)度器*/private ThreadPoolTaskScheduler scheduler;/*** 任務(wù)列表*/private List<ITask> taskes;private final Map<String, ScheduledTaskHolder> register = new ConcurrentHashMap<>();public ScheduledTaskRegistrar(ThreadPoolTaskScheduler scheduler) {this.scheduler = scheduler;}/*** 注冊(cè)任務(wù)*/public void register() {for (ITask task : taskes) {ScheduledFuture<?> future = this.scheduler.schedule(task, new CronTrigger(task.getCron()));ScheduledTaskHolder holder = new ScheduledTaskHolder();holder.setScheduledFuture(future);holder.setTask(task);register.put(task.getClass().getName(), holder);}}/*** 查詢所有任務(wù)** @return*/public Collection<ScheduledTaskHolder> list() {return register.values();}/*** 立即執(zhí)行任務(wù)** @param className*/public void start(String className) {ScheduledTaskHolder holder = register.get(className);if (holder != null) {holder.getTask().run();}}/*** 暫停任務(wù)** @param className*/public void pause(String className) {ScheduledTaskHolder holder = register.get(className);if (holder != null) {if(holder.terminate()){return;}ScheduledFuture<?> future = holder.getScheduledFuture();future.cancel(true);}}/***重啟任務(wù)* @param className* @param cron*/public void restart(String className,String cron){ScheduledTaskHolder holder = register.get(className);if (holder != null) {if(!holder.terminate()){//暫停原任務(wù)holder.getScheduledFuture().cancel(true);}ITask task = holder.getTask();if(!StringUtils.isEmpty(cron)){task.setCron(cron);}ScheduledFuture<?> future = this.scheduler.schedule(task, new CronTrigger(task.getCron()));holder.setScheduledFuture(future);}}public void setTaskes(List<ITask> taskes) {this.taskes = taskes;}private void log(){register.forEach((k,v)->{LOGGER.info("register {} complete,cron {}",k,v.getTask().getCron());});}@Overridepublic void afterPropertiesSet(){register();log();}
}
3、任務(wù)包裝類ScheduledTaskHolder
任務(wù)包裝類包含具體任務(wù)的實(shí)例、任務(wù)執(zhí)行的ScheduledFuture以及任務(wù)的運(yùn)行狀態(tài)。
package com.bbs.config.scheduled;import java.util.concurrent.ScheduledFuture;/*** @Author whh* @Date: 2023/03/15/ 19:45* @description*/
public class ScheduledTaskHolder {/*** 具體任務(wù)*/private ITask task;/***result of scheduling*/private ScheduledFuture<?> scheduledFuture;public ITask getTask() {return task;}public void setTask(ITask task) {this.task = task;}public ScheduledFuture<?> getScheduledFuture() {return scheduledFuture;}public void setScheduledFuture(ScheduledFuture<?> scheduledFuture) {this.scheduledFuture = scheduledFuture;}public boolean terminate() {return scheduledFuture.isCancelled();}
}
4、 任務(wù)抽象類ITask
任務(wù)抽象類ITask實(shí)現(xiàn)了Runnable接口同時(shí)提供了抽象方法execute用于自定義任務(wù)實(shí)現(xiàn),同時(shí)對(duì)任務(wù)進(jìn)行了增強(qiáng),增加了任務(wù)執(zhí)行信息的打印。
package com.bbs.config.scheduled;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;/*** @Author whh* @Date: 2023/03/15/ 19:46* @description* 任務(wù)的抽象類* 用于做切面打印任務(wù)執(zhí)行的時(shí)間,耗時(shí)等信息*/
public abstract class ITask implements Runnable{private static final Logger LOGGER = LoggerFactory.getLogger("ITask");private String cron;private String taskName;private String taskDescription;public ITask(String cron) {this.cron = cron;}@Overridepublic void run() {LocalDateTime start = LocalDateTime.now();execute();LocalDateTime end = LocalDateTime.now();Duration duration = Duration.between(start, end);long millis = duration.toMillis();Date date = new CronTrigger(this.cron).nextExecutionTime(new SimpleTriggerContext());LOGGER.info("任務(wù):[{}]執(zhí)行完畢,開(kāi)始時(shí)間:{},結(jié)束時(shí)間:{},耗時(shí):{}ms,下次執(zhí)行時(shí)間{}",this.taskName, start,end,millis,date);}/*** 用戶的任務(wù)實(shí)現(xiàn)*/public abstract void execute();public String getCron() {return cron;}public void setCron(String cron) {this.cron = cron;}public String getTaskName() {return taskName;}public void setTaskName(String taskName) {this.taskName = taskName;}public String getTaskDescription() {return taskDescription;}public void setTaskDescription(String taskDescription) {this.taskDescription = taskDescription;}
}
二、前端控制器TaskController
package com.bbs.task.controller;import com.bbs.config.scheduled.ITask;
import com.bbs.config.scheduled.ScheduledTaskRegistrar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.SimpleTriggerContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** @Author whh* @Date: 2023/03/15/ 21:18* @description*/
@RestController
@RequestMapping("/task")
public class TaskController {@Autowiredprivate ScheduledTaskRegistrar scheduledTaskRegistrar;@GetMappingpublic List<Map<String,Object>> listTask(){return scheduledTaskRegistrar.list().stream().map(e->{Map<String,Object> temp = new HashMap<>();ITask task = e.getTask();temp.put("任務(wù)名",task.getTaskName());temp.put("任務(wù)描述",task.getTaskDescription());temp.put("cron表達(dá)式",task.getCron());temp.put("任務(wù)狀態(tài)",e.terminate()?"已停止":"運(yùn)行中");temp.put("任務(wù)類名",task.getClass().getName());temp.put("下次執(zhí)行時(shí)間",new CronTrigger(task.getCron()).nextExecutionTime(new SimpleTriggerContext()));return temp;}).collect(Collectors.toList());}@PostMapping("/pause")public void pause(String className){scheduledTaskRegistrar.pause(className);}@PostMapping("/start")public void start(String className){scheduledTaskRegistrar.start(className);}@PostMapping("/restart")public void restart(String className,String cron){scheduledTaskRegistrar.restart(className,cron);}
}
三、自定義任務(wù)實(shí)現(xiàn)
package com.bbs.task;import com.bbs.config.scheduled.ITask;/*** @Author whh* @Date: 2023/03/15/ 21:16* @description*/
public class MonitorTask extends ITask {public MonitorTask(String cron) {super(cron);}@Overridepublic void execute() {System.out.println("hello world ....");}
}
四、測(cè)試
1、應(yīng)用啟動(dòng)時(shí)控制臺(tái)會(huì)打印我們注冊(cè)的任務(wù)信息
2、任務(wù)執(zhí)行情況
3、前端控制器查詢?nèi)蝿?wù)
調(diào)用暫停API后任務(wù)停止執(zhí)行,重啟任務(wù)后任務(wù)又重新開(kāi)始執(zhí)行。