Фоновые задания и планирование
Выполняйте медленные операции вне HTTP-запросов. Elang предоставляет очередь заданий с JSON-данными в базе данных, отложенные задания, ограниченные повторные попытки, просмотр неудачных заданий и планирование по UTC. Одно подключение к базе данных позволяет совместно зафиксировать изменение данных приложения и добавление соответствующего задания в очередь.
Подготовка служебных таблиц
Стартовые проекты аутентификации, блога, панели управления и API включают миграцию. В другом приложении выполните:
php elang queue:setup
Проверьте изменения схемы перед использованием --force в рабочей среде. SQLite удобен локально. Для нескольких серверов приложения используйте общую базу данных MySQL или PostgreSQL.
Регистрация обработчика
Регистрируйте обработчики при начальной загрузке приложения или в провайдере сервисов. HTTP-процессы и обработчики очереди должны загружать одинаковые регистрации. Полезная нагрузка содержит данные JSON; Elang никогда не десериализует объекты PHP и не принимает имя класса из данных задания.
use Elang\Queue\Jobs;
use Psr\Log\LoggerInterface;
$app->container->get(Jobs::class)->register(
'reports.generate',
function (array $payload, LoggerInterface $logger): void {
// Load the record by ID and authorize the business operation here.
$logger->info('Report requested', ['report_id' => $payload['report_id']]);
}
);
Добавление и выполнение заданий
use Elang\Queue\Queue;
$id = $app->container->get(Queue::class)->push(
'reports.generate',
['report_id' => 42],
delay: 10
);
php elang queue:work
php elang queue:work --once
php elang queue:work --stop-when-empty --max-jobs=100
php elang queue:failed
php elang queue:retry JOB_ID
Задания резервируются с помощью условного обновления базы данных и токена резервирования. Гарантия доставки — как минимум один раз: аварийное завершение обработчика очереди или истечение срока резервирования может привести к новой попытке. Делайте обработчики идемпотентными с помощью уникальных ограничений на уровне приложения и ключей идемпотентности внешних провайдеров.
По умолчанию срок резервирования составляет 120 секунд, а максимальное число попыток — пять. Настройте queue.lease_seconds и queue.max_attempts в config/app.php. Длительность обработчика должна быть меньше срока резервирования; тайм-ауты диспетчера процессов также задавайте меньше этого срока, а долгие отчёты разбивайте на ограниченные части. Elang не прерывает выполняющийся PHP-обработчик и не продлевает его резервирование автоматически. Задания с истёкшим резервированием могут быть зарезервированы повторно; старое резервирование не может подтвердить выполнение задания, которое уже обрабатывает новый процесс.
Повторные попытки используют экспоненциально растущую задержку с пределом в один час. Списки неудачных заданий показывают класс исключения и метаданные, но не полезную нагрузку или сообщения исключений. Данные заданий находятся в закрытом хранилище базы данных; передавайте идентификаторы записей вместо секретов. Диспетчер процессов должен перезапускать обработчики очереди после развёртывания и неожиданных завершений. --max-jobs ограничивает цикл работы обработчика заданным числом успешных попыток; обработчик, который раз за разом обнаруживает пустую очередь, продолжает работать.
Совместная фиксация данных предметной области и задания
$database->connection()->transactional(function ($connection) use ($queue): void {
$connection->insert('reports', ['status' => 'pending']);
$queue->push('reports.generate', ['report_id' => (int) $connection->lastInsertId()]);
});
Обе операции должны использовать одно и то же внедрённое подключение Database. Исключение откатывает их вместе. Внешние HTTP-вызовы, доставка почты и отдельные базы данных находятся за пределами этой транзакции.
Планирование заданий
use Elang\Queue\Schedule;
$schedule = $app->container->get(Schedule::class);
$schedule->everyMinute('reports.minute', 'reports.generate', ['report_id' => 42]);
$schedule->dailyAt('reports.daily', '02:00', 'reports.generate', ['report_id' => 43]);
Запускайте php elang schedule:run раз в минуту через cron. Время задаётся в UTC. Общий уникальный ключ запуска в базе данных не позволяет двум процессам планировщика поставить в очередь одно расписание с тем же ID в одну и ту же минуту. Это не препятствует наложению долгого задания на следующий запуск по расписанию. Если такое наложение недопустимо, добавьте блокировку приложения. Пропущенные минуты не восполняются.
Продолжите изучение с событий в реальном времени и архитектуры крупных приложений.