web-dev-qa-db-ja.com

SpringでEnableSchedulingアノテーションを使用して実行時にスケジュールされたタスクを再開するにはどうすればよいですか?

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";
    }

}
13
Paul Fournel
  1. 注入されるシングルトンBeanを作成します TaskScheduler 。これは、private ScheduledFuture job1;のように、すべて ScheduledFutures を状態変数として保持します。
  2. 展開時に、データベースからすべてのスケジュールデータをロードしてジョブを開始し、job1などのすべての状態変数を入力します。
  3. スケジューリングデータの変更時に、 cancel 対応するFuture(例:job1)を実行し、新しいスケジューリングデータで再開します。

ここでの重要なアイデアは、作成時にFuturesを制御して、それらをいくつかの状態変数に保存し、スケジューリングデータの何かが変更されたときにそれらをキャンセルできるようにすることです。

作業コードは次のとおりです。

applicationContext.xml

<task:annotation-driven />
<task:scheduler id="infScheduler" pool-size="10"/>

Futuresを保持するシングルトン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);
                }
        }
}
7
Andrei I

簡単なアプローチの1つは、スケジューラーをキャンセルまたは再起動するのではなく、新しいタスクのみを追加することです。

構成が変更されるたびに、新しい構成で新しいタスクを追加するだけです。

次に、タスクを実行するたびに、まず何らかの状態をチェックして(データベースにクエリを実行するか、並行マップを検索するなど)、最新バージョンかどうかを判断する必要があります。そうである場合は、続行する必要があります。それ以外の場合は、すぐに終了する必要があります。

唯一の欠点は、実行頻度と比較してジョブ構成を頻繁に変更する場合、もちろん、スケジュールされたタスクのリストがメモリ内で増え続けることです。

0
drrob

Set<ScheduledTask> ScheduledTaskRegistrar.getScheduledTasks()を使用してすべてのスケジュールタスクを取得し、ScheduledTask::cancel()を呼び出すのはどうですか?または、ThreadPoolTaskScheduler::shutdown()を実行してThreadPoolTask​​Schedulerを再作成し、ScheduledTaskRegistrarで再設定しますか?

0
razor