Статьи

Планировщики Java EE

Серверы приложений Java EE имеют встроенную поддержку планирования, и в большинстве приложений нет необходимости включать внешние зависимости, такие как знаменитая библиотека планировщика Quartz .

Служба таймера Java EE 6 , доступная в полном профиле Java EE 6 и 7, дает нам множество опций для определения интервала планирования и того, что произойдет, если мы остановим и перезапустим приложение, которое содержит наш планировщик.

Планировщик Java EE может быть:

  • постоянный : сервер приложений сохраняет события планирования, когда приложение не работает, чтобы не потерять их
  • автоматический : простое определение планировщика, большая часть деталей обрабатывается сервером приложений
  • программный : у нас есть полный контроль над всеми параметрами планировщика.

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

1. Разрешено ли пропустить некоторые мероприятия по расписанию?

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

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

Это постоянный планировщик.

Примечание : сервер приложений будет генерировать все пропущенные события при (пере) запуске приложения. Этот пакет событий настраивается по частоте и задержке. Смотрите подробности в документации к серверу приложений.

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

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

Напротив, постоянный планировщик выживает до перезапуска приложения; он просто спит, когда приложение не запущено.

Как выбрать?

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

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

2. Будет ли приложение работать в кластере?

В кластере запущено более одного экземпляра нашего приложения (по одному экземпляру на узел кластера), и все экземпляры имеют свою собственную копию нашего планировщика.

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

Каждый сервер приложений имеет свой собственный способ решения проблемы «множественных экземпляров планировщика» (например, см. [Ссылка 2] для WebSphere), но, как правило, требуется, чтобы планировщик был постоянным при использовании кластера.

3. Должен ли программируемый интервал составляться на производстве?

Еще один важный вопрос, на который нужно ответить: сможем ли мы изменить расписание после развертывания приложения?

Если параметры планирования (его частота) являются фиксированными, автоматический планировщик является лучшим решением, поскольку очень просто кодировать: одну аннотацию (или несколько строк XML, если вы предпочитаете старый способ).

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

Помнить:

  • расписание автоматического планировщика определяется во время сборки
  • расписание программного планировщика определяется во время запуска приложения

Автоматический планировщик

Определить автоматический планировщик очень просто:

  1. Создать синглтон EJB, выполняемый при запуске
  2. Создайте метод, который будет вызываться при каждом событии планирования

Примечание: полный код можно найти в проекте статьи [см. Ссылку 3].

Первый шаг:

1
2
3
@Startup
@Singleton
public class MyScheduler

Аннотация @ javax.ejb.Startup просит контейнер EJB создать EJB (и наш планировщик) при запуске приложения.

Аннотация @ javax.ejb.Singleton заставляет контейнер EJB создавать только один экземпляр.

Важно: планировщик используется сервером приложений (контейнер EJB); он никогда не должен создаваться остальной частью кода приложения.

Затем нам нужен метод, который будет вызываться при планировании событий:

1
2
@Schedule(/** scheduling parameters */)
public void doSomeThing() {..}

Метод должен быть публичным и возвращать void.

Аннотация @ javax.ejb.Schedule определяет:

  • интервал планирования в формате cron [см. ссылку 4]
  • имя планировщика (в приложении может быть много планировщиков)
  • постоянный логический флаг, который определяет, является ли планировщик постоянным или нет

Например:

1
2
3
4
5
@Schedule(
    minute = "*/15",
    hour = "*",
    info = "15MinScheduler",
    persistent = false )

который определяет непостоянный планировщик, который запускается каждые 15 минут.

Посмотрите классы AutomaticPersistentScheduler и AutomaticNonPersistentScheduler в проекте статьи [ссылка 3] для полного примера.

Примечание : есть также аннотация @Schedules [см. Ссылку 1], которая позволяет определять несколько определений @Schedule.

Это полезно, когда есть требования к расписанию, которые нельзя выразить в одном определении cron.

Программный планировщик

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

У нас есть больше шагов:

  1. Создать синглтон EJB, выполняемый при запуске
  2. Поиск ресурса TimerService
  3. Создайте планировщик при инициализации EJB
  4. Создать метод @Timeout

Первый шаг аналогичен автоматическому планировщику:

1
2
3
@Startup
@Singleton
public class MyScheduler

Затем (второй шаг) нам нужно найти службу таймера сервера приложений, но внедрение помогает нам:

1
2
@Resource
private TimerService timerService;

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

В нашем случае служба Timer будет использоваться для создания нового планировщика следующим образом (третий шаг):

1
2
3
4
5
String minuteSchedule = "*/15";
String hourSchedule = "*";
ScheduleExpression schedule = new ScheduleExpression()
 .minute(minuteSchedule)
 .hour(hourSchedule);

Javax.ejb.ScheduleExpression определяет расписание cron [см. Ссылку 4] подобно аннотации @Schedule.

Очень важное различие между @Schedule и ScheduleExpression заключается в том, что первое исправляется во время сборки: для изменения параметров расписания (например, каждые 15 минут или каждые 30 минут) нам нужно изменить код класса, а затем снова собрать и развернуть приложение. ,

В последнем случае (SchedulerExpression) параметры расписания (в приведенном выше примере переменные minuteSchedule и hourSchedule) могут быть определены и изменены при запуске приложения, например, с помощью minutesSchedule и hourSchedule, например:
файл свойств или подключенная СУБД.

1
2
3
TimerConfig timerConfig = new TimerConfig();
timerConfig.setInfo("ProgrammaticPersistentScheduler");
timerConfig.setPersistent(true);

Javax.ejb.TimerConfig дает нам возможность определить имя планировщика (setInfo (String)) и, если оно является постоянным или нет (setPersistent (boolean)).

Используя ScheduleExpression и экземпляр TimerConfig, мы можем использовать службу Timer для создания планировщика (точнее, календарного таймера).

1
timerService.createCalendarTimer(schedule, timerConfig);

Метод createCalendarTime () возвращает экземпляр javax.ejb.Timer, который можно использовать для опроса таймера, например, когда произойдет следующее будущее событие или даже для его уничтожения.
планировщик

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

1
2
@Timeout
public void doSomeThing() {..}

Метод должен быть публичным и возвращать void.

И у нас есть наш планировщик и работает.

Выводы

Стандарт Java EE предоставляет нам множество опций для определения планировщика, который периодически запускает наш код. Нет необходимости в дополнительных зависимостях проекта.

связи

  1. Руководство по Oracle EE6 по API службы таймера
  2. IBM WebSphere 8.x Создание таймеров с использованием сервиса таймера EJB для корпоративных компонентов
  3. Статья проекта на GitHub
  4. Cron в Википедии