Java 8と春。 この質問 は非常に役に立ちましたが、問題を完全に解決することはできませんでした。実行時にジョブの頻度を変更する方法を調査してきました。
これで、次にジョブを実行する日付を構成できます。ただし、遅延を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
。これは、private ScheduledFuture job1;
のように、すべて ScheduledFuture
s を状態変数として保持します。job1
などのすべての状態変数を入力します。Future
(例:job1
)を実行し、新しいスケジューリングデータで再開します。ここでの重要なアイデアは、作成時にFuture
sを制御して、それらをいくつかの状態変数に保存し、スケジューリングデータの何かが変更されたときにそれらをキャンセルできるようにすることです。
作業コードは次のとおりです。
applicationContext.xml
<task:annotation-driven />
<task:scheduler id="infScheduler" pool-size="10"/>
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);
}
}
}
簡単なアプローチの1つは、スケジューラーをキャンセルまたは再起動するのではなく、新しいタスクのみを追加することです。
構成が変更されるたびに、新しい構成で新しいタスクを追加するだけです。
次に、タスクを実行するたびに、まず何らかの状態をチェックして(データベースにクエリを実行するか、並行マップを検索するなど)、最新バージョンかどうかを判断する必要があります。そうである場合は、続行する必要があります。それ以外の場合は、すぐに終了する必要があります。
唯一の欠点は、実行頻度と比較してジョブ構成を頻繁に変更する場合、もちろん、スケジュールされたタスクのリストがメモリ内で増え続けることです。
Set<ScheduledTask> ScheduledTaskRegistrar.getScheduledTasks()
を使用してすべてのスケジュールタスクを取得し、ScheduledTask::cancel()
を呼び出すのはどうですか?または、ThreadPoolTaskScheduler::shutdown()
を実行してThreadPoolTaskSchedulerを再作成し、ScheduledTaskRegistrarで再設定しますか?