Рубрики
Без рубрики

Планирование Задач Весенней Загрузки

Планирование задач или их повторение через определенные промежутки времени часто является полезной функцией. В этой статье мы рассмотрим возможности планирования Spring Boot.

Автор оригинала: Guest Contributor.

Вступление

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

Поскольку Spring Boot предлагает несколько вариантов, мы рассмотрим и реализуем их все.

Настройка проекта

Как обычно, когда дело доходит до создания приложений Spring Boot, создание скелетного проекта проще всего с помощью Spring Initializr . Вам не нужны никакие дополнительные зависимости для включения планирования.

Чтобы включить планирование, все, что нам нужно сделать, это аннотировать наш основной класс:

@SpringBootApplication
@EnableScheduling
public class SampleScheduleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SampleScheduleApplication.class, args);
    }
}

Аннотация @EnableScheduling позволяет контейнеру Spring замечать любые @Запланированные аннотации в компонентах, управляемых Spring.

Условное Планирование

Есть еще один способ включить планирование – с помощью аннотации @ConditionalOnProperty . Это позволяет нам “включать” и “выключать” ваши классы конфигурации, задав свойство в классе application.properties .

Для этого давайте создадим новый класс и снабдим его аннотациями @EnableScheduling , @Configuration и @ConditionalOnProperty аннотации:

@EnableScheduling
@Configuration
@ConditionalOnProperty(name = "spring.enable.scheduling")
public class ScheduleEnabling {}

Теперь в файле application.properties давайте добавим наше новое свойство и установим для него значение true :

spring.enable.scheduling = true

Просто изменив эту переменную, мы можем включать и выключать функциональность.

Чтобы выполнить метод по расписанию, вам необходимо аннотировать его аннотацией @По расписанию . Изменение параметров аннотации определит, выполняется ли она с фиксированной скоростью, с фиксированной задержкой, пользовательскими интервалами и т.д.

Планирование с фиксированной ставкой и строкой фиксированной ставки

Чтобы запланировать выполнение метода с фиксированной скоростью, мы добавим соответствующий параметр в нашу аннотацию – @Scheduled(фиксированная скорость) . Этот параметр принимает целые числа, выраженные в миллисекундах. Поэтому, если вам нужна скорость в 1 секунду, скорость должна быть введена как 1000 так как значение равно миллисекундам.

В качестве альтернативы вы можете использовать параметр @По расписанию(строка с фиксированной ставкой) для экстернализации суммы с помощью строковой переменной среды.

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

Давайте установим переменную в файле application.properties :

sample.schedule.string = PT2S

Префикс PT является стандартом ISO-8601 для обозначения длительности или периодов , и с его помощью мы можем вызвать sample.schedule.string , чтобы запланировать вызов метода за 2 секунды.

Это также позволяет нам указывать различные задержки для разных используемых профилей:

@Scheduled(fixedRateString = "${sample.schedule.string}")
public void scheduleTaskWithFixedRate() throws InterruptedException {
    task1();
    task2();
}

public void task1() throws InterruptedException {
    logger.info("Task 1 starts" + Thread.currentThread());
    Thread.sleep(1000);
    logger.info("Task 1 ends" + Thread.currentThread());
}

public void task2() throws InterruptedException {
    logger.info("Task 2 starts" + Thread.currentThread());
    Thread.sleep(1000);
    logger.info("Task 2 ends" + Thread.currentThread());
}

Давайте запустим этот фрагмент кода:

Однако здесь мы видим проблему:

Задание 1 начинается в 00:01:44 и заканчивается в 00:01:45, как и ожидалось. Задание 2 начинается в 00:01:45 и заканчивается в 00:01:46, как и ожидалось. Задание 1 начинается в 00:01:46 и заканчивается в 00:01:47.

Поскольку оба task1() и task2() были выполнены, вы ожидаете, что метод будет ждать еще 2 секунды для повторного запуска.

Задачи с фиксированной скоростью не ждут завершения предыдущего выполнения, они просто вызывают метод с определенной скоростью. Поскольку для завершения методов task1() и task2 () требуется 2 секунды , метод вызывается снова одновременно с завершением этих двух.

Это может стать проблемой в среде с несколькими запланированными задачами.

Учтите это – выполнение задачи 1() занимает 1 минуту, а выполнение задачи 2() занимает 5 секунд. Поскольку оба они выполняются в одном потоке, может возникнуть ситуация, когда задача 1() начнет обработку и заблокирует поток. Это не позволит выполнить задачу 2 () , даже если фиксированная скорость составляет 5 секунд.

В этом сценарии нам необходимо увеличить количество потоков, доступных в нашем пуле потоков для планирования. Spring предоставляет свойство, которым мы можем управлять, чтобы указать размер: spring.task.scheduling.pool.size – значение по умолчанию равно 1.

Мы можем использовать фиксированную ставку, когда определенную задачу необходимо выполнять повторно, но каждая задача не зависит от другой. Кроме того, будьте осторожны, чтобы не выполнять тяжелые задачи без надлежащей скорости, так как незавершенность может привести к неприятной ошибке OutOfMemoryError .

Планирование с фиксированной задержкой и строкой фиксированной задержки

/| фиксированная задержка работает очень похоже на фиксированную скорость . Но разница здесь в том, что фиксированная задержка ожидает завершения предыдущего выполнения, чтобы начать следующее. Представьте себе сценарий, в котором выполнение вашей функции занимает 1 секунду, а вы дали фиксированную задержку в 2 секунды.

Git Essentials

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

Это, в свою очередь, займет в общей сложности 3 секунды.

В журналах ниже вы можете четко видеть, что разница между двумя последующими задачами составляет 3 секунды. Это включает в себя фиксированное время задержки в 1 секунду, а также 2 секунды, которые мы дали в качестве сна:

@Scheduled(fixedDelayString = "${sample.schedule.string}")
public void scheduleTaskWithFixedDelay() throws InterruptedException {
    task1();
}

public void task1() throws InterruptedException {
    logger.info("Task 1 starts" + Thread.currentThread());
    Thread.sleep(1000);
    logger.info("Task 1 ends" + Thread.currentThread());
}

Давайте запустим этот фрагмент кода:

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

Этот вариант не требует особых объяснений, поскольку он используется в сочетании с двумя вышеперечисленными. Начальная задержка, как справедливо следует из названия, обеспечивает начальную задержку для первого выполнения.

Если у вас есть функция с начальной задержкой 2 секунды и фиксированной скоростью 1 секунда, то первое выполнение будет отложено на 2 секунды, а функция будет выполняться каждые 1 секунду после этого:

@Scheduled(initialDelay = 1000, fixedRateString = "${sample.schedule.string}")
public void scheduleTaskWithInitialDelay() throws InterruptedException {
    task1();
}

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

Настраиваемые Временные Интервалы

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

Но до сих пор мы видели только очень общие примеры для ставок. Может возникнуть ситуация, когда нам понадобятся очень конкретные временные интервалы. Вот, пользовательские cron выражения .

Выражения Cron

Большинство разработчиков, вероятно, слышали о утилите cron в Linux. Это демонический процесс, который запускается без вмешательства пользователя и выполняет задачи.

Синтаксис выражений cron в утилите cron и синтаксис выражений cron для планирования в основном схожи.

Выражения Cron-это в основном строки, которые описывают детали расписания. Это обеспечивает гораздо больший контроль, чем предыдущие 2 метода:

Секунд Да 0-59 , – * /
Минут Да 0-59 , – * /
Часов Да 0-23 , – * /
День месяца Да 1-31 , – * / Д Ш К
Месяц Да 0-11 или ЯНВАРЬ-ДЕКАБРЬ , – * /
День недели Да 1-7 или ВС-СБ , – * / Л C #
Год Нет пустой или 1970-2099 , – * /

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

Выражения Cron могут быть очень простыми, но также и очень сложными. Четкое понимание ценностей облегчит игру с ними.

За исключением поля год, все остальные поля являются обязательными:

      

Пример: 0 0 12 * * ? 2019 – Это выражение cron срабатывает в 12 часов дня, каждый день месяца, за каждый месяц, за 2019 год.

Для некоторых общих значений вы также можете использовать предопределенные аннотации:

  • @reboot : Запланируйте метод для каждой перезагрузки приложения
  • @@ежегодно |/| @ежегодно : Запланируйте запуск метода один раз в год @ежемесячно
  • : Запланируйте запуск метода один раз в месяц @еженедельно
  • : Запланируйте запуск метода один раз в неделю @ежедневно
  • / @полночь : Запланируйте выполнение метода один раз в день @почасово
  • : Запланируйте выполнение метода один раз в час

Давайте напишем пример кода для этого:

public void doSomething() {
    // Something
}

При планировании важно учитывать часовые пояса – проклятие каждого разработчика, который работает со временем.

Скорее всего, вы захотите установить флаг зона для определенного региона. Например, мы будем запускать этот метод в 12 вечера каждый день в 2019 году, в зависимости от часового пояса Парижа:

public void doSomething() {
    // Something
}

Вы можете найти все часовые пояса в официальных документах Oracle .

Конечно, вы также можете экстернализировать выражения cron с помощью файла application.properties :

cron.expression= 0 0 12 * * ? 2019

А затем призовите его через:

public void doSomething() {
    // Something
}

Вы также можете использовать сайт, такой как FreeFormatter , для создания выражения cron, задав входные параметры. Это очень полезно для тех, кто новичок в создании выражений cron.

Вывод

В этой статье мы рассмотрели, как мы можем планировать задачи с помощью Spring Boot. Самым большим преимуществом использования Spring Boot является простота, с которой мы можем реализовать планирование. Мало того, он также предлагает несколько вариантов, чтобы мы могли выбрать то, что соответствует нашим требованиям.

Планировщики являются важными компонентами большинства приложений, поскольку они отправляют важную по времени, а также специфичную для пользователя информацию по мере необходимости. Теперь ты знаешь, как это сделать!