I have been investigating how to change the frequency of a job on runtime with Java 8 and spring. This question was very useful but it did not totally solve my issue.
我一直在研究如何使用Java 8和spring在运行时更改作业的频率。这个问题很有用,但并不能完全解决我的问题。
I can now configure the date when to job should be executed next. But If set the delay to 1 year, then I need to wait 1 year before the new configuration in taken into account.
现在,我可以配置下一次执行作业的日期。但是如果将延迟设置为1年,那么我需要等待1年才能考虑新的配置。
My idea would be to stop the scheduled task if the configuration value is changed (so from another class). Then recalculate the next time the task should be executed. Perhaps there is an easier way of doing this.
我的想法是,如果配置值发生了变化(从另一个类中),则停止调度任务。然后重新计算下一次应该执行的任务。也许有一种更简单的方法。
Here is the code I have so far.
这是我到目前为止的代码。
@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();
}
}
);
}
}
This would be what I would like to do.
这就是我想做的。
@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";
}
}
2 个解决方案
#1
5
- Create a singleton bean that gets an injected
TaskScheduler
. This will hold as state variables allScheduledFuture
s, likeprivate ScheduledFuture job1;
- 创建一个单例bean,它获得一个注入的任务调度程序。这将作为状态变量所有的时间,如私有的ScheduledFuture job1;
- On deployment, load from databases all schedule data and start the jobs, filling in all state variables like
job1
. - 在部署时,从数据库加载所有调度数据并启动作业,填充所有状态变量,如job1。
- On change of scheduling data, cancel the corresponding
Future
(e.gjob1
) and then start it again with the new scheduling data. - 改变调度数据时,取消相应的未来(e)。然后使用新的调度数据重新启动它。
The key idea here is to get control on the Future
s as they are created, so to save them in some state variables, so that when something in scheduling data changes, you can cancel them.
这里的关键思想是在创建期货时控制它们,以便将它们保存在一些状态变量中,以便当调度数据时发生变化时,可以取消它们。
Here is the working code:
以下是工作代码:
applicationContext.xml
中
<task:annotation-driven />
<task:scheduler id="infScheduler" pool-size="10"/>
The singleton bean, that holds the 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);
}
}
}
#2
0
One simple approach is to only ever add new tasks, not to try and cancel or restart the scheduler.
一种简单的方法是只添加新任务,而不是尝试取消或重新启动调度程序。
Each time the configuration changes, just add a new task with its new configuration.
每次配置更改时,只需添加一个带有新配置的新任务。
Then, whenever a task runs, it must first check some state (by querying database, or lookup up in a concurrent map, or whatever) to decide if it is the latest version. If it is, then it should proceed. Otherwise, it should end immediately.
然后,每当任务运行时,它必须首先检查某个状态(通过查询数据库,或在并发映射中查找,或其他),以确定它是否是最新的版本。如果是,那么它应该继续下去。否则,它应该立即结束。
The only downside is that if you are changing job configuration frequently compared to how often they run, then of course the list of scheduled tasks will keep growing in memory.
唯一的缺点是,如果您频繁地更改作业配置,与它们运行的频率相比,那么当然,计划的任务列表将在内存中不断增长。
#1
5
- Create a singleton bean that gets an injected
TaskScheduler
. This will hold as state variables allScheduledFuture
s, likeprivate ScheduledFuture job1;
- 创建一个单例bean,它获得一个注入的任务调度程序。这将作为状态变量所有的时间,如私有的ScheduledFuture job1;
- On deployment, load from databases all schedule data and start the jobs, filling in all state variables like
job1
. - 在部署时,从数据库加载所有调度数据并启动作业,填充所有状态变量,如job1。
- On change of scheduling data, cancel the corresponding
Future
(e.gjob1
) and then start it again with the new scheduling data. - 改变调度数据时,取消相应的未来(e)。然后使用新的调度数据重新启动它。
The key idea here is to get control on the Future
s as they are created, so to save them in some state variables, so that when something in scheduling data changes, you can cancel them.
这里的关键思想是在创建期货时控制它们,以便将它们保存在一些状态变量中,以便当调度数据时发生变化时,可以取消它们。
Here is the working code:
以下是工作代码:
applicationContext.xml
中
<task:annotation-driven />
<task:scheduler id="infScheduler" pool-size="10"/>
The singleton bean, that holds the 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);
}
}
}
#2
0
One simple approach is to only ever add new tasks, not to try and cancel or restart the scheduler.
一种简单的方法是只添加新任务,而不是尝试取消或重新启动调度程序。
Each time the configuration changes, just add a new task with its new configuration.
每次配置更改时,只需添加一个带有新配置的新任务。
Then, whenever a task runs, it must first check some state (by querying database, or lookup up in a concurrent map, or whatever) to decide if it is the latest version. If it is, then it should proceed. Otherwise, it should end immediately.
然后,每当任务运行时,它必须首先检查某个状态(通过查询数据库,或在并发映射中查找,或其他),以确定它是否是最新的版本。如果是,那么它应该继续下去。否则,它应该立即结束。
The only downside is that if you are changing job configuration frequently compared to how often they run, then of course the list of scheduled tasks will keep growing in memory.
唯一的缺点是,如果您频繁地更改作业配置,与它们运行的频率相比,那么当然,计划的任务列表将在内存中不断增长。