SpringBoot動態(tài)定時功能實現(xiàn)方案詳解
業(yè)務場景
基于上篇程序,做了一版動態(tài)定時程序,然后發(fā)現(xiàn)這個定時程序需要在下次執(zhí)行的時候會加載新的時間,所以如果改了定時程序不能馬上觸發(fā),所以想到一種方法,在保存定時程序的時候?qū)ron表達式傳過去,然后觸發(fā)定時程序,下面看看怎么實現(xiàn)
環(huán)境準備
開發(fā)環(huán)境
- JDK 1.8
- SpringBoot2.2.1
- Maven 3.2+
開發(fā)工具
- IntelliJ IDEA
- smartGit
- Navicat15
實現(xiàn)方案
基于上一版進行改進:
- 先根據(jù)選擇的星期生成cron表達式,保存到數(shù)據(jù)庫里,同時更改cron表達式手動觸發(fā)定時程序加載最新的cron表達式
- 根據(jù)保存的cron表達式規(guī)則執(zhí)行定時程序
- 通過CommandLineRunner設置啟動加載
- 加上線程池,提高線程復用率和程序性能
加上ThreadPoolTaskScheduler,支持同步和異步兩種方式:
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@Slf4j
public class ScheduleConfig implements SchedulingConfigurer , AsyncConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
}
@Bean(destroyMethod="shutdown" , name = "taskScheduler")
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("itemTask-");
scheduler.setAwaitTerminationSeconds(600);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
@Bean(name = "asyncExecutor")
public ThreadPoolTaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setQueueCapacity(1000);
executor.setKeepAliveSeconds(600);
executor.setMaxPoolSize(20);
executor.setThreadNamePrefix("itemAsyncTask-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public Executor getAsyncExecutor() {
return asyncExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
log.error("異步任務異常,message {} , method {} , params" , throwable , method , objects);
};
}
}加上一個SchedulerTaskJob接口:
public interface SchedulerTaskJob{
void executeTask();
}
AbstractScheduler 抽象類,提供基本的功能
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
@Slf4j
@Component
@Data
public abstract class AbstractScheduler implements SchedulerTaskJob{
@Resource(name = "taskScheduler")
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Override
public void executeTask() {
String cron = getCronString();
Runnable task = () -> {
// 執(zhí)行業(yè)務
doBusiness();
};
Trigger trigger = new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
CronTrigger trigger;
try {
trigger = new CronTrigger(cron);
return trigger.nextExecutionTime(triggerContext);
} catch (Exception e) {
log.error("cron表達式異常,已經(jīng)啟用默認配置");
// 配置cron表達式異常,執(zhí)行默認的表達式
trigger = new CronTrigger(getDefaultCron());
return trigger.nextExecutionTime(triggerContext);
}
}
};
threadPoolTaskScheduler.schedule(task , trigger);
}
protected abstract String getCronString();
protected abstract void doBusiness();
protected abstract String getDefaultCron();
}實現(xiàn)類,基于自己的業(yè)務實現(xiàn),然后事項抽象類,通過模板模式進行編程
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
@Service
@Slf4j
@Data
public class ItemSyncScheduler extends AbstractScheduler {
@Value("${configtask.default.itemsync}")
private String defaultCron ;
private String cronString ;
@Override
protected String getCronString() {
if (StrUtil.isNotBlank(cronString)) return cronString;
SyncConfigModel configModel = syncConfigService.getOne(Wrappers.<SyncConfigModel>lambdaQuery()
.eq(SyncConfigModel::getBizType, 1)
.last("limit 1"));
if (configModel == null) return defaultCron;
return configModel.getCronStr();
}
@Override
protected void doBusiness() {
log.info("執(zhí)行業(yè)務...");
log.info("執(zhí)行時間:{}" , LocalDateTime.now());
// 執(zhí)行業(yè)務
}
@Override
protected String getDefaultCron() {
return defaultCron;
}
}如果更改了cron表達式,程序不會馬上觸發(fā),所以直接開放一個接口出來,調(diào)用的時候,設置最新的表達式,然后重新調(diào)用定時程序
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class ItemSchedulerController {
private ItemSyncScheduler itemSyncScheduler;
@Autowired
public ItemSchedulerController(ItemSyncScheduler itemSyncScheduler) {
this.itemSyncScheduler= itemSyncScheduler;
}
@GetMapping(value = "/updateItemCron")
@ApiOperation(value = "更新cron表達式")
public void updateItemCron(@RequestParam("cronString") String cronString) {
log.info("更新cron表達式...");
log.info("cronString:{}" , cronString);
itemSyncScheduler.setCronString(cronString);
itemSyncScheduler.executeTask();
}
}實現(xiàn)CommandLineRunner ,實現(xiàn)Springboot啟動加載
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Order(1)
public class SchedulerTaskRunner implements CommandLineRunner {
private ItemSyncScheduler itemSyncScheduler;
@Autowired
public SchedulerTaskRunner(ItemSyncScheduler itemSyncScheduler) {
this.itemSyncScheduler= itemSyncScheduler;
}
@Override
public void run(String... args) throws Exception {
itemSyncScheduler.executeTask();
}
}歸納總結(jié)
基于上一版定時程序的問題,做了改進,加上了線程池和做到了動態(tài)觸發(fā),網(wǎng)上的資料很多都是直接寫明使用SchedulingConfigurer來實現(xiàn)動態(tài)定時程序,不過很多都寫明場景,本文通過實際,寫明實現(xiàn)方法,本文是在保存定時程序的時候,設置最新的cron表達式,調(diào)一下接口重新加載,還可以使用canal等中間件監(jiān)聽數(shù)據(jù)表,如果改了就再設置cron表達式,然后觸發(fā)程序
到此這篇關(guān)于SpringBoot動態(tài)定時功能實現(xiàn)方案詳解的文章就介紹到這了,更多相關(guān)SpringBoot動態(tài)定時內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Spring?Boot如何限制在一分鐘內(nèi)某個IP只能訪問10次
有些時候,為了防止我們上線的網(wǎng)站被攻擊,或者被刷取流量,我們會對某一個ip進行限制處理,這篇文章,我們將通過Spring?Boot編寫一個小案例,來實現(xiàn)在一分鐘內(nèi)同一個IP只能訪問10次,感興趣的朋友一起看看吧2023-10-10
詳談ThreadLocal-單例模式下高并發(fā)線程安全
這篇文章主要介紹了ThreadLocal-單例模式下高并發(fā)線程安全,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
slf4j?jcl?jul?log4j1?log4j2?logback各組件系統(tǒng)日志切換
這篇文章主要介紹了slf4j、jcl、jul、log4j1、log4j2、logback的大總結(jié),各個組件的jar包以及目前系統(tǒng)日志需要切換實現(xiàn)方式的方法,有需要的朋友可以借鑒參考下2022-03-03
FeignClient如何通過配置變量調(diào)用配置文件url
這篇文章主要介紹了FeignClient如何通過配置變量調(diào)用配置文件url,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06

