如何在Spring中使用EnableScheduling注释在运行时重新启动计划任务?
我一直在研究如何使用Java 8和spring更改运行时作业的频率。 这个问题非常有用,但并没有完全解决我的问题。
我现在可以配置下一次应该执行作业的日期。 但是如果将延迟设置为1年,那么我需要在考虑新配置之前等待1年。
我的想法是在配置值发生变化时停止计划任务(所以从另一个类开始)。 然后在下次执行任务时重新计算。 也许有一种更简单的方法可以做到这一点。
这是我到目前为止的代码。
@Configuration @EnableScheduling public class RequestSchedulerConfig implements SchedulingConfigurer { @Autowired SchedulerConfigService schedulerConfigService; @Bean public RequestScheduler myBean() { return new RequestScheduler(); } @Bean(destroyMethod = "shutdown") public Executor taskExecutor() { return Executors.newScheduledThreadPool(100); } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(taskExecutor()); taskRegistrar.addTriggerTask( new Runnable() { @Override public void run() { myBean().startReplenishmentComputation(); } }, new Trigger() { @Override public Date nextExecutionTime(TriggerContext triggerContext) { Duration d = schedulerConfigService.getIntervalFromDB(); return DateTime.now().plus(d).toDate(); } } ); } }
这就是我想做的事情。
@RestController @RequestMapping("/api/config/scheduler") public class RequestSchedulerController { @Autowired ApplicationConfigWrapper applicationConfigWrapper; @RequestMapping("/set/") @ResponseBody public String setRequestSchedulerConfig(@RequestParam(value = "frequency", defaultValue = "") final String frequencyInSeconds){ changeValueInDb(frequencyInSeconds); myJob.restart(); return "Yeah"; } }
- 创建一个获取注入的
TaskScheduler
的单例bean。 这将作为状态变量保存所有ScheduledFuture
,如private ScheduledFuture job1;
- 在部署时,从数据库加载所有计划数据并启动作业,填写所有状态变量,如
job1
。 - 在更改调度数据时, 取消相应的
Future
(例如job1
),然后使用新的调度数据再次启动它。
这里的关键思想是在创建Future
控制它们,以便将它们保存在某些状态变量中,这样当调度数据中的某些内容发生变化时,您可以取消它们。
这是工作代码:
applicationContext.xml中
持有Future
s的单例bean
@Component public class SchedulerServiceImpl implements SchedulerService { private static final Logger logger = LoggerFactory.getLogger(SchedulerServiceImpl.class); @Autowired @Qualifier(value="infScheduler") private TaskScheduler taskScheduler; @Autowired private MyService myService; private ScheduledFuture job1;//for other jobs you can add new private state variables //Call this on deployment from the ScheduleDataRepository and everytime when schedule data changes. @Override public synchronized void scheduleJob(int jobNr, long newRate) {//you are free to change/add new scheduling data, but suppose for now you only want to change the rate if (jobNr == 1) {//instead of if/else you could use a map with all job data if (job1 != null) {//job was already scheduled, we have to cancel it job1.cancel(true); } //reschedule the same method with a new rate job1 = taskScheduler.scheduleAtFixedRate(new ScheduledMethodRunnable(myService, "methodInMyServiceToReschedule"), newRate); } } }
一种简单的方法是只添加新任务,而不是尝试取消或重新启动调度程序。
每次配置更改时,只需使用新配置添加新任务。
然后,每当任务运行时,它必须首先检查某个状态(通过查询数据库,或在并发映射中查找,或其他任何)来确定它是否是最新版本。 如果是,那么它应该继续。 否则,它应该立即结束。
唯一的缺点是,如果您经常更改作业配置与其运行频率相比,那么当然,计划任务列表将在内存中不断增长。