在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] - 动态定时任务开始...