Задачи по расписанию
Некоторую работу сервис должен делать сам, без запроса от клиента: каждую ночь удалять старые комментарии, раз в час пересчитывать статистику, каждые несколько секунд писать в лог состояние. Такие задачи по расписанию в Quarkus описывают аннотацией @Scheduled на методе бина. Quarkus сам вызывает метод в нужное время в отдельном потоке.
Подключение
./mvnw quarkus:add-extension -Dextensions="scheduler"Задача с интервалом
// src/main/java/com/example/blog/BlogJobs.java
package com.example.blog;
import io.quarkus.logging.Log;
import io.quarkus.scheduler.Scheduled;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class BlogJobs {
@Scheduled(every = "10s")
void reportPosts() {
Log.infof("Постов в блоге: %d", Post.count());
}
}every = "10s" — запускать каждые 10 секунд. Можно писать 30s, 5m, 1h. Log — удобный логгер Quarkus: не нужно объявлять поле логгера в каждом классе. Метод не принимает параметров и ничего не возвращает. В dev mode каждые 10 секунд в терминале появляется строка:
INFO [com.example.blog.BlogJobs] (vert.x-worker-thread-1) Постов в блоге: 2
Задача по cron
Для запуска в конкретное время используют cron-выражение — строку из полей «секунды минуты часы день месяц день-недели»:
// src/main/java/com/example/blog/BlogJobs.java
@Inject
CommentRepository commentRepository;
@Scheduled(cron = "0 0 3 * * ?")
@Transactional
void deleteOldComments() {
long deleted = commentRepository.delete("createdAt < ?1", LocalDateTime.now().minusDays(30));
Log.infof("Удалено старых комментариев: %d", deleted);
}Задача запускается каждый день в 03:00:00 и удаляет комментарии старше 30 дней. Импорты — jakarta.inject.Inject, jakarta.transaction.Transactional и java.time.LocalDateTime. Задача меняет данные, поэтому ей нужна @Transactional, как и методу ресурса.
| Выражение | Когда |
|---|---|
0 0 3 * * ? | Каждый день в 03:00 |
0 */15 * * * ? | Каждые 15 минут |
0 0 9 ? * MON-FRI | По будням в 09:00 |
0 0 0 1 * ? | Первого числа каждого месяца в полночь |
Расписание из настроек
Интервал и cron можно вынести в application.properties, чтобы менять их без пересборки:
// src/main/java/com/example/blog/BlogJobs.java
@Scheduled(cron = "{blog.cleanup-cron}")# src/main/resources/application.properties
blog.cleanup-cron=0 0 3 * * ?Фигурные скобки в аннотации означают «взять значение из настроек». В нашем проекте есть BlogConfig с префиксом blog, а он не пропускает незнакомые ключи blog.* (ошибка SRCFG00050 из урока о config mapping). Поэтому добавьте в интерфейс метод для нового ключа:
// src/main/java/com/example/blog/BlogConfig.java
String cleanupCron();Где посмотреть задачи
В Dev UI на карточке Scheduler есть раздел Scheduled methods: список всех задач, их расписание и кнопка, чтобы запустить задачу прямо сейчас, не дожидаясь срока. Это удобно для проверки ночной очистки днём.
Частая ошибка
Cron в Quarkus — в формате Quartz, где первое поле — секунды, а полей шесть или семь. Привычное выражение Unix-cron из пяти полей, например 0 3 * * *, не подходит, и приложение не запустится:
IllegalStateException: Invalid cron() expression: @Scheduled(cron = "0 3 * * *") declared on com.example.blog.BlogJobs#deleteOldComments()
Caused by: java.lang.IllegalArgumentException: Cron expression contains 5 parts but we expect one of [6, 7]
Добавьте секунды в начало и ? в одно из полей дня: 0 0 3 * * ?.