1. Обзор
Spring предоставляет простой в реализации API для планирования заданий. Он отлично работает, пока мы не развернем несколько экземпляров нашего приложения. Spring по умолчанию не может обрабатывать синхронизацию планировщика по нескольким экземплярам — вместо этого он выполняет задания одновременно на каждом узле.
В этом кратком руководстве мы рассмотрим ShedLock — библиотеку Java, которая гарантирует, что наши запланированные задачи запускаются только один раз в одно и то же время , и является альтернативой Quartz .
2. Зависимости Maven
Чтобы использовать ShedLock с Spring, нам нужно добавить зависимость shedlock-
spring : [
](https://search.maven.org/search?q=g:net.javacrumbs.shedlock%20AND%20a:shedlock-spring&core=gav)
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>2.2.0</version>
</dependency>
3. Конфигурация
Обратите внимание, что ShedLock работает только в средах с общей базой данных при объявлении правильного LockProvider
. Он создает таблицу или документ в базе данных, где хранится информация о текущих блокировках.
В настоящее время ShedLock поддерживает Mongo, Redis, Hazelcast, ZooKeeper и все, что имеет драйвер JDBC.
В этом примере мы будем использовать базу данных H2 в памяти. Чтобы это заработало, нам нужно предоставить базу данных H2 и JDBC-зависимость ShedLock :
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-jdbc-template</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
Далее нам нужно создать таблицу базы данных для ShedLock, чтобы хранить информацию о блокировках планировщика:
CREATE TABLE shedlock (
name VARCHAR(64),
lock_until TIMESTAMP(3) NULL,
locked_at TIMESTAMP(3) NULL,
locked_by VARCHAR(255),
PRIMARY KEY (name)
)
Мы должны объявить источник данных в файле свойств нашего приложения Spring Boot, чтобы bean- компонент DataSource
мог быть Autowired
. В этом примере мы используем application.yml
для определения источника данных базы данных H2:
spring:
datasource:
driverClassName: org.h2.Driver
url: jdbc:h2:mem:shedlock_DB;INIT=CREATE SCHEMA IF NOT EXISTS shedlock;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
Давайте настроим LockProvider
с приведенной выше конфигурацией источника данных. Spring может сделать это довольно просто:
@Configuration
public class SchedulerConfiguration {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}
Еще одно требование к конфигурации, которое мы должны предоставить, — это аннотации @EnableScheduling
и @EnableSchedulerLock
в нашем классе конфигурации Spring:
@SpringBootApplication
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class Application {
public static void main(String[] args) {
SpringApplication.run(SpringApplication.class, args);
}
}
Параметр defaultLockAtMostFor
указывает количество времени по умолчанию, в течение которого блокировка должна сохраняться в случае отказа исполняющего узла. Он использует формат продолжительности ISO8601 .
В следующем разделе мы увидим, как переопределить это значение по умолчанию.
4. Создание задач
Чтобы создать запланированную задачу, обрабатываемую ShedLock, мы просто помещаем аннотации @Scheduled
и @SchedulerLock
в метод:
@Component
class ForEachTaskScheduler {
@Scheduled(cron = "0 0/15 * * * ?")
@SchedulerLock(name = "TaskScheduler_scheduledTask",
lockAtLeastForString = "PT5M", lockAtMostForString = "PT14M")
public void scheduledTask() {
// ...
}
}
Во-первых, давайте посмотрим на @Scheduled
. Он поддерживает формат cron
, причем это выражение означает «каждые 15 минут».
Затем, взглянув на @SchedulerLock,
параметр имени
должен быть уникальным, и для этого обычно достаточно ClassName_methodName .
Мы не хотим, чтобы одновременно выполнялось более одного запуска этого метода, и ShedLock использует для этого уникальное имя.
Мы также добавили несколько необязательных параметров.
Во- первых, мы добавили lockAtLeastForString
, чтобы можно было установить некоторое расстояние между вызовами методов. Использование «PT5M»
означает, что этот метод будет удерживать блокировку как минимум на 5 минут. Другими словами, это означает, что этот метод может запускаться ShedLock не чаще, чем каждые пять минут.
Затем мы добавили lockAtMostForString
, чтобы указать, как долго должна сохраняться блокировка в случае выхода из строя исполняющего узла. Использование «PT14M»
означает, что он будет заблокирован не более чем на 14 минут.
В обычных ситуациях ShedLock снимает блокировку сразу после завершения задачи. Теперь нам не нужно было это делать, потому что в @EnableSchedulerLock есть значение по умолчанию ,
но мы решили переопределить его здесь.
5. Вывод
В этой статье мы узнали, как создавать и синхронизировать запланированные задачи с помощью ShedLock.
Как всегда, весь исходный код доступен на GitHub .