在Springboot中使用輕量級計划任務框架,實現靜態定時任務、動態定時任務及手動注入定時任務。
首先,模塊化定時任務配置類,作為定時任務入口:
1 package cn.demo.support.config; 2 3 import java.util.concurrent.Executor; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.ScheduledThreadPoolExecutor; 6 7 import org.slf4j.Logger; 8 import org.slf4j.LoggerFactory; 9 import org.springframework.context.annotation.Bean; 10 import org.springframework.context.annotation.Configuration; 11 import org.springframework.scheduling.annotation.EnableScheduling; 12 import org.springframework.scheduling.annotation.SchedulingConfigurer; 13 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 14 import org.springframework.scheduling.config.ScheduledTaskRegistrar; 15 16 import cn.jsfund.ngdp.support.batchSchedule.AutoRegistScheduleTask; 17 import cn.jsfund.ngdp.support.batchSchedule.HiveClusterAsync; 18 19 /** 20 * 定時任務配置 21 * 22 */ 23 @EnableScheduling 24 @Configuration 25 public class ScheduleConfig implements SchedulingConfigurer { 26 27 private static final Logger logger = LoggerFactory.getLogger(ScheduleConfig.class); 28 29 @Bean 30 public HiveClusterAsync initHiveClusterAsync() { 31 32 logger.info("開啟Hive集群同步定時任務..."); 33 return new HiveClusterAsync();//動態定時任務,掃庫取crontab執行 34 } 35 36 @Bean 37 public AutoRegistScheduleTask initAutoScheduler() { 38 39 logger.info("開啟自定義定時任務..."); 40 return new AutoRegistScheduleTask();//一般定時 41 } 42 43 //注入計划任務注冊器 44 @Bean 45 public ScheduledTaskRegistrar scheduledTaskRegistrar() { 46 47 return new ScheduledTaskRegistrar(); 48 } 49 50 @Override 51 public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { 52 taskRegistrar.setScheduler(taskExecutor()); 53 } 54 55 /** 56 * 配置多線程執行定時任務,不配置的話,定時任務是默認單線程順序執行 57 */ 58 // @Bean(destroyMethod = "shutdown") 59 // public Executor taskExecutor() { 60 // return Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() * 2);//return ScheduledThreadPoolExecutor 61 // } 62 63 //因為需要手動注入定時任務,連接池換成spring的 ThreadPoolTaskScheduler,調用其schedule(Runnable task, Trigger trigger)方法即可。此類封裝了 jdk的ScheduledThreadPoolExecutor 64 @Bean(destroyMethod = "shutdown") 65 public ThreadPoolTaskScheduler taskExecutor() { 66 ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler(); 67 executor.setPoolSize(20); 68 executor.setThreadNamePrefix("taskExecutor-"); 69 executor.setWaitForTasksToCompleteOnShutdown(true); 70 executor.setAwaitTerminationSeconds(60); 71 return executor; 72 } 73 }
1.靜態定時任務
若單線程執行定時任務,則可以不需要以上配置,啟動類上加@EnableScheduling注解即可,然后定時類直接使用@Scheduled注解,:
1 package cn.demo.support.batchSchedule; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.scheduling.annotation.Scheduled; 6 import org.springframework.scheduling.annotation.SchedulingConfigurer; 7 import org.springframework.scheduling.config.ScheduledTaskRegistrar; 8 import org.springframework.stereotype.Component; 9 10 import cn.jsfund.ngdp.support.web.service.bigdata.impl.HiveClusterAsyncServiceImpl; 11 12 //@Component 不需要模塊化入口類引入時,需加上此注解注入bean 13 public class AutoRegistScheduleTask { 14 15 private static final Logger logger = LoggerFactory.getLogger(AutoRegistScheduleTask.class); 16 17 @Scheduled(cron = "0/3 * * * * ?") 18 public void exeDemo1() throws Exception { 19 logger.info("開始執行1"); 20 //do somthing 21 logger.info("執行1結束"); 22 } 23 24 @Scheduled(cron = "0/3 * * * * ?") 25 public void exeDemo2() throws Exception { 26 logger.info("開始執行2"); 27 //do somthing 28 logger.info("執行2結束"); 29 } 30 }
2.動態定時任務
需要實現SchedulingConfigurer 接口,重寫方法添加定時任務:
1 package cn.demo.support.batchSchedule; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.scheduling.annotation.SchedulingConfigurer; 6 import org.springframework.scheduling.config.ScheduledTaskRegistrar; 7 8 public class HiveClusterAsync implements SchedulingConfigurer { 9 10 private static final Logger logger = LoggerFactory.getLogger(HiveClusterAsync.class); 11 12 @Override 13 public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { 14 15 //查庫獲取crongtab 16 String cron = "0/8 * * * * ?"; 17 taskRegistrar.addCronTask(new Runnable() { 18 19 public void run() { 20 logger.info("動態定時任務開始..."); 21 //dosomething 22 } 23 24 }, cron); 25 logger.info("添加定時任務完成"); 26 }}
或者使用addTriggerTask方法添加計划任務,區別是,addTriggerTask方法內的nextExecutionTime方法返回值為crontab匹配的下次執行時間,
nextExecutionTime方法每次都會執行,可以修改其返回值,改變下次執行時間,
比如使用場景為:多節點部署的代碼,保證定時任務高可用,每個節點都需要刷新當前內存中注冊的定時任務,我們就可使用定時刷新,但是刷新一定要不同步的,
保證刷新時,其他節點能正常執行任務,因此,可以給下次執行時間每次都加個隨機值,使多節點不同時間刷新計划任務;
1 package cn.demo.support.batchSchedule; 2 3 import java.util.Date; 4 import java.util.Random; 5 6 import javax.annotation.Resource; 7 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 import org.springframework.scheduling.Trigger; 11 import org.springframework.scheduling.TriggerContext; 12 import org.springframework.scheduling.annotation.SchedulingConfigurer; 13 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 14 import org.springframework.scheduling.config.ScheduledTaskRegistrar; 15 import org.springframework.scheduling.support.CronTrigger; 16 import org.springframework.stereotype.Component; 17 18 @Component 19 public class TimingRefreshHiveTask implements SchedulingConfigurer { 20 21 @Resource 22 ThreadPoolTaskScheduler taskExecutor; 23 24 public TimingRefreshHiveTask() { 25 } 26 27 private static final Logger logger = LoggerFactory.getLogger(HiveClusterSyncScheduler.class); 28 29 @Override 30 public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { 31 taskRegistrar.setScheduler(taskExecutor); 32 33 String crontab = "0/30 * * * * ?"; 34 35 taskRegistrar.addTriggerTask(new Runnable() { 36 37 @Override 38 public void run() { 39 logger.info("do something, eg: 刷新已注冊的定時任務..."); 40 } 41 }, new Trigger() { 42 43 @Override 44 public Date nextExecutionTime(TriggerContext triggerContext) { 45 Date date = new CronTrigger(crontab).nextExecutionTime(triggerContext); 46 logger.info("已配置下次刷新時間為:" + date); 47 48 Date nexDate = new Date(); 49 nexDate.setTime((3 + new Random().nextInt(5)) * 1000 + date.getTime());//給每次執行時間隨機+ 3~8秒 50 logger.info("增加隨機值后,下次刷新時間:" + nexDate); 51 return nexDate; 52 } 53 }); 54 55 } 56 57 }
系統啟動后執行效果如下:可以看到下次執行時,按照修改后的時間執行。
[2021-04-07 17:32:41.042] INFO [restartedMain] NgdpMgrApplication[line:34] - 當前節點:10.0.192.100 [2021-04-07 17:33:05.003] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:49] - do something, eg: 刷新已注冊的定時任務... [2021-04-07 17:33:05.004] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:56] - 已配置下次刷新時間為:Wed Apr 07 17:33:30 CST 2021 [2021-04-07 17:33:05.005] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:60] - 增加隨機值后,下次刷新時間:Wed Apr 07 17:33:34 CST 2021 [2021-04-07 17:33:34.004] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:49] - do something, eg: 刷新已注冊的定時任務... [2021-04-07 17:33:34.005] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:56] - 已配置下次刷新時間為:Wed Apr 07 17:34:00 CST 2021 [2021-04-07 17:33:34.005] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:60] - 增加隨機值后,下次刷新時間:Wed Apr 07 17:34:07 CST 2021 [2021-04-07 17:34:07.004] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:49] - do something, eg: 刷新已注冊的定時任務... [2021-04-07 17:34:07.005] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:56] - 已配置下次刷新時間為:Wed Apr 07 17:34:30 CST 2021 [2021-04-07 17:34:07.005] INFO [taskExecutor-1] HiveClusterSyncScheduler[line:60] - 增加隨機值后,下次刷新時間:Wed Apr 07 17:34:37 CST 2021
3.手動注入定時任務
業務類中引入ThreadPoolTaskScheduler
@Autowired private ThreadPoolTaskScheduler threadPoolTaskScheduler;
調用其schedule方法,此方法和上面的addTriggerTask方法入參相同;
String crontab = "0 48 16 * * ?"; threadPoolTaskScheduler.schedule(new Runnable() { @Override public void run() { try { //do something; } catch (ServiceException e) { logger.info("手動添加的任務計划執行失敗,異常信息:", e); } } }, new Trigger() { @Override public Date nextExecutionTime(TriggerContext triggerContext) { return new CronTrigger(crontab).nextExecutionTime(triggerContext); } });
執行效果:
[2021-03-26 11:06:27.001] INFO [pool-2-thread-4] AutoRegistScheduleTask[line:19] - 開始執行1 [2021-03-26 11:06:27.001] INFO [pool-2-thread-5] AutoRegistScheduleTask[line:26] - 開始執行2 [2021-03-26 11:06:27.001] INFO [pool-2-thread-4] AutoRegistScheduleTask[line:21] - 執行1結束 [2021-03-26 11:06:27.001] INFO [pool-2-thread-5] AutoRegistScheduleTask[line:28] - 執行2結束 [2021-03-26 11:06:30.001] INFO [pool-2-thread-4] AutoRegistScheduleTask[line:19] - 開始執行1 [2021-03-26 11:06:30.001] INFO [pool-2-thread-7] AutoRegistScheduleTask[line:26] - 開始執行2 [2021-03-26 11:06:30.001] INFO [pool-2-thread-4] AutoRegistScheduleTask[line:21] - 執行1結束 [2021-03-26 11:06:30.001] INFO [pool-2-thread-7] AutoRegistScheduleTask[line:28] - 執行2結束 [2021-03-26 11:06:32.002] INFO [pool-2-thread-6] HiveClusterAsync[line:55] - 動態定時任務開始... [2021-03-26 11:06:33.002] INFO [pool-2-thread-1] AutoRegistScheduleTask[line:19] - 開始執行1 [2021-03-26 11:06:33.002] INFO [pool-2-thread-8] AutoRegistScheduleTask[line:26] - 開始執行2 [2021-03-26 11:06:33.002] INFO [pool-2-thread-1] AutoRegistScheduleTask[line:21] - 執行1結束 [2021-03-26 11:06:33.003] INFO [pool-2-thread-8] AutoRegistScheduleTask[line:28] - 執行2結束 [2021-03-26 11:06:36.001] INFO [pool-2-thread-1] AutoRegistScheduleTask[line:19] - 開始執行1 [2021-03-26 11:06:36.001] INFO [pool-2-thread-4] AutoRegistScheduleTask[line:26] - 開始執行2 [2021-03-26 11:06:36.001] INFO [pool-2-thread-1] AutoRegistScheduleTask[line:21] - 執行1結束 [2021-03-26 11:06:36.001] INFO [pool-2-thread-4] AutoRegistScheduleTask[line:28] - 執行2結束 [2021-03-26 11:06:39.000] INFO [pool-2-thread-1] AutoRegistScheduleTask[line:19] - 開始執行1 [2021-03-26 11:06:39.000] INFO [pool-2-thread-1] AutoRegistScheduleTask[line:21] - 執行1結束 [2021-03-26 11:06:39.000] INFO [pool-2-thread-1] AutoRegistScheduleTask[line:26] - 開始執行2 [2021-03-26 11:06:39.000] INFO [pool-2-thread-1] AutoRegistScheduleTask[line:28] - 執行2結束 [2021-03-26 11:06:40.001] INFO [pool-2-thread-6] HiveClusterAsync[line:55] - 動態定時任務開始...