Как перезапустить запланированную задачу во время выполнения с помощью аннотации EnableScheduling в spring?

Я изучаю, как изменить частоту задания во время выполнения с помощью 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";
    }

}

Ответ 1

  • Создайте синглтон bean, который получает введенный TaskScheduler. Это будет содержать переменные состояния ScheduledFuture s, например private ScheduledFuture job1;
  • При развертывании загрузите из баз данных все данные расписания и запустите задания, заполнив все переменные состояния, такие как job1.
  • При изменении данных планирования cancel соответствующий Future (например, job1), а затем запустите его снова с помощью нового планирование данных.

Основная идея здесь - получить контроль над Future по мере их создания, чтобы сохранить их в некоторых переменных состояния, так что когда что-то в планировании данных изменится, вы можете отменить их.

Вот рабочий код:

applicationContext.xml

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

Синглетный bean, содержащий Future s

@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

Один простой подход заключается только в том, чтобы добавлять новые задачи, а не пытаться отменить или перезапустить планировщик.

При каждом изменении конфигурации просто добавьте новую задачу с новой конфигурацией.

Затем, всякий раз, когда запускается задача, она должна сначала проверить состояние (путем запроса базы данных или поиска на параллельной карте или что-то еще), чтобы решить, является ли она последней версией. Если да, то это должно продолжаться. В противном случае он должен немедленно прекратиться.

Единственным недостатком является то, что если вы часто меняете конфигурацию задания по сравнению с тем, как часто они запускаются, то, конечно, список запланированных задач будет продолжать расти в памяти.