Как происходит запуск метода с аннотацией @Scheduled и как работает scheduler
JavaScheduledSpringпланировщик задачфреймворки
Что проверяют
Знаете ли вы, что планировщик Spring по умолчанию однопоточный и что произойдёт с расписанием, когда приложение поднято в нескольких экземплярах. Это две ошибки, которые ловятся уже на проде, а не в тестах.
Как отвечать
@EnableSchedulingвключаетScheduledAnnotationBeanPostProcessor. На старте он обходит бины, находит методы с@Scheduled(без аргументов, обычноvoid) и регистрирует их вTaskScheduler. Приватный метод или объект, созданный черезnew, планировщик не увидит.- Дефолтный
TaskSchedulerэто пул из одного потока: все задачи приложения выстраиваются в очередь друг за другом, и одна долгая блокирует остальные. Лечитсяspring.task.scheduling.pool.sizeили своимThreadPoolTaskScheduler. fixedDelayотсчитывает паузу от завершения предыдущего запуска,fixedRateот его начала. Параллельно один и тот же метод не запускается: если выполнение длиннее периода, следующие срабатывания просто опаздывают и идут подряд.cronв Spring состоит из шести полей, первое это секунды, в отличие от пятиполевого cron в Unix. Поддерживаются макросы вида@dailyи параметрzone, иначе берётся часовой пояс сервера.
@Scheduled(cron = "0 0 3 * * *", zone = "Europe/Moscow")
public void cleanup() { archiveService.purgeOldRows(); }- Исключение из задачи Spring логирует своим
ErrorHandler, расписание продолжает работать. У гологоScheduledExecutorServiceнеобработанное исключение отменило бы все последующие запуски. - В кластере задача выполнится в каждом экземпляре. Нужен внешний замок: ShedLock, Quartz с JDBC JobStore или выделенный инстанс.
Где спотыкаются
Уверены, что каждая задача получает отдельный поток, и не понимают, почему ночная выгрузка сдвинула все остальные задания.
Пишут cron из пяти полей и получают расписание, сдвинутое на разряд, либо ошибку разбора выражения на старте.
Забывают про несколько реплик, поэтому рассылка уходит пользователям дважды или трижды.
Не ставят таймаут на внешние вызовы внутри задачи: зависший запрос держит единственный поток планировщика бесконечно.