Подтвердить что ты не робот

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

Я создал простую запланированную задачу, используя аннотацию Spring Framework @Scheduled.

 @Scheduled(fixedRate = 2000)
 public void doSomething() {}

Теперь я хочу остановить эту задачу, когда она больше не нужна.

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

Есть ли что-то, что Spring предоставляет, чтобы остановить задачу @Scheduled?

4b9b3361

Ответ 1

Вариант 1. Использование постпроцессора

postProcessBeforeDestruction(Object bean, String beanName) ScheduledAnnotationBeanPostProcessor и явным образом вызовите postProcessBeforeDestruction(Object bean, String beanName) для компонента, планирование которого должно быть остановлено.


Вариант 2: Сохранение карты целевых компонентов в их будущем.

private final Map<Object, ScheduledFuture<?>> scheduledTasks =
        new IdentityHashMap<>();

@Scheduled(fixedRate = 2000)
public void fixedRateJob() {
    System.out.println("Something to be done every 2 secs");
}

@Bean
public TaskScheduler poolScheduler() {
    return new CustomTaskScheduler();
}

class CustomTaskScheduler extends ThreadPoolTaskScheduler {

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
        ScheduledFuture<?> future = super.scheduleAtFixedRate(task, period);

        ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
        scheduledTasks.put(runnable.getTarget(), future);

        return future;
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
        ScheduledFuture<?> future = super.scheduleAtFixedRate(task, startTime, period);

        ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
        scheduledTasks.put(runnable.getTarget(), future);

        return future;
    }
}

Когда планирование для bean-компонента должно быть остановлено, вы можете найти карту, чтобы получить соответствующее Future и явно отменить ее.

Ответ 2

В этом вопросе есть немного двусмысленности.

  • Когда вы говорите "остановите эту задачу", вы имели в виду остановиться таким образом, чтобы впоследствии ее можно было восстановить (если да, программно, используя условие, которое возникает в одном приложении или внешнем состоянии?)
  • Выполняете ли вы какие-либо другие задачи в одном контексте? (Возможность отключения всего приложения, а не задачи). В этом случае вы можете использовать конечную точку исполнительного механизма.

Мое лучшее предположение: вы хотите закрыть задачу, используя условие, которое может возникнуть в одном приложении, в восстанавливаемом режиме. Я попытаюсь ответить на основании этого предположения.

Это самое простое возможное решение, о котором я могу думать. Однако я сделаю некоторые улучшения, такие как раннее возвращение, а не вложенное, если s

@Component
public class SomeScheduledJob implements Job {

    private static final Logger LOGGER = LoggerFactory.getLogger(SomeScheduledJob.class);

    @Value("${jobs.mediafiles.imagesPurgeJob.enable}")
    private boolean imagesPurgeJobEnable;

    @Override
    @Scheduled(cron = "${jobs.mediafiles.imagesPurgeJob.schedule}")
    public void execute() {

        if(!imagesPurgeJobEnable){
            return;
        }
        Do your conditional job here...
   }

Свойства для указанного кода

jobs.mediafiles.imagesPurgeJob.enable=true or false
jobs.mediafiles.imagesPurgeJob.schedule=0 0 0/12 * * ?

Ответ 3

Некоторое время назад у меня было это требование в моем проекте, что любой компонент должен иметь возможность создать новую запланированную задачу или остановить планировщик (все задачи). Поэтому я сделал что-то вроде этого

@Configuration
@EnableScheduling
@ComponentScan
@Component
public class CentralScheduler {

    private static AnnotationConfigApplicationContext CONTEXT = null;

    @Autowired
    private ThreadPoolTaskScheduler scheduler;

    public static CentralScheduler getInstance() {
        if (!isValidBean()) {
            CONTEXT = new AnnotationConfigApplicationContext(CentralScheduler.class);
        }

        return CONTEXT.getBean(CentralScheduler.class);
    }

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        return new ThreadPoolTaskScheduler();
    }

    public void start(Runnable task, String scheduleExpression) throws Exception {
        scheduler.schedule(task, new CronTrigger(scheduleExpression));
    }

    public void start(Runnable task, Long delay) throws Exception {
        scheduler.scheduleWithFixedDelay(task, delay);
    }

    public void stopAll() {
        scheduler.shutdown();
        CONTEXT.close();
    }

    private static boolean isValidBean() {
        if (CONTEXT == null || !CONTEXT.isActive()) {
            return false;
        }

        try {
            CONTEXT.getBean(CentralScheduler.class);
        } catch (NoSuchBeanDefinitionException ex) {
            return false;
        }

        return true;
    }
}

Поэтому я могу делать такие вещи, как

Runnable task = new MyTask();
CentralScheduler.getInstance().start(task, 30_000L);
CentralScheduler.getInstance().stopAll();

Имейте в виду, что по некоторым причинам я сделал это, не беспокоясь о concurrency. В противном случае должна быть некоторая синхронизация.

Ответ 4

Вот пример, где мы можем остановить, запустить и перечислить также все запланированные задачи:

@RestController
@RequestMapping("/test")
public class TestController {

private static final String SCHEDULED_TASKS = "scheduledTasks";

@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;

@Autowired
private ScheduledTasks scheduledTasks;

@Autowired
private ObjectMapper objectMapper;

@GetMapping(value = "/stopScheduler")
public String stopSchedule(){
    postProcessor.postProcessBeforeDestruction(scheduledTasks, SCHEDULED_TASKS);
    return "OK";
}

@GetMapping(value = "/startScheduler")
public String startSchedule(){
    postProcessor.postProcessAfterInitialization(scheduledTasks, SCHEDULED_TASKS);
    return "OK";
}

@GetMapping(value = "/listScheduler")
public String listSchedules() throws JsonProcessingException{
    Set<ScheduledTask> setTasks = postProcessor.getScheduledTasks();
    if(!setTasks.isEmpty()){
        return objectMapper.writeValueAsString(setTasks);
    }else{
        return "No running tasks !";
    }
}

}

Ответ 5

Запланированные

Когда spring процесс Scheduled, он будет выполнять итерацию каждого метода, аннотирует эту аннотацию и организует задачи beans, как показывает следующий источник:

private final Map<Object, Set<ScheduledTask>> scheduledTasks =
        new IdentityHashMap<Object, Set<ScheduledTask>>(16);

Отмена

Если вы просто хотите отменить повторенную запланированную задачу, вы можете просто сделать следующее (здесь runnable demo в моем репо ):

@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;
@Autowired
private TestSchedule testSchedule;

public void later() {
    postProcessor.postProcessBeforeDestruction(test, "testSchedule");
}

Примечание

Он найдет этот beans ScheduledTask и отменит его один за другим. Следует заметить, что это также остановит текущий текущий метод (как показывает postProcessBeforeDestruction источник).

    synchronized (this.scheduledTasks) {
        tasks = this.scheduledTasks.remove(bean); // remove from future running
    }
    if (tasks != null) {
        for (ScheduledTask task : tasks) {
            task.cancel(); // cancel current running method
        }
    }