Настройка Redis для реализации очередей задач
Очереди задач нужны там, где операция слишком долгая для HTTP-запроса: отправка email, генерация PDF, обработка изображений, синхронизация с внешними API. Вместо того чтобы делать это синхронно и держать соединение, задача ставится в очередь Redis и выполняется воркером в фоне. Пользователь получает ответ немедленно.
Структуры данных Redis для очередей
Redis поддерживает несколько подходов к реализации очередей:
List + LPUSH/RPOP (FIFO) — самый простой. LPUSH queue task добавляет в голову, RPOP queue забирает с хвоста. Проблема: воркер в цикле делает RPOP — если очередь пустая, тратит CPU на пустые запросы.
BLPOP (Blocking List Pop) — RPOP с блокировкой: воркер ждёт появления задачи без polling:
BLPOP queue1 queue2 0 # 0 = ждать бесконечно
# При появлении задачи возвращает [queue_name, value]
Sorted Set для отложенных задач — задачи с временем выполнения хранятся в sorted set со score = timestamp:
ZADD delayed_queue 1704067200 "task_payload" # Unix timestamp
# Воркер периодически проверяет:
ZRANGEBYSCORE delayed_queue 0 NOW LIMIT 0 10
ZREM delayed_queue "task_payload"
Redis Streams — более мощный механизм с группами потребителей, подтверждениями (ACK), replay. Рекомендуется для серьёзных продакшен-систем.
Laravel Queue с Redis
Laravel Queue — абстракция поверх различных backend'ов. Redis — один из лучших вариантов: быстрый, поддерживает приоритеты, delayed jobs, failed jobs.
config/queue.php:
'default' => env('QUEUE_CONNECTION', 'redis'),
'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'queue',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90, // Секунды до повторной попытки если воркер упал
'block_for' => 5, // Секунды блокировки BLPOP
'after_commit' => true, // Ставить в очередь после коммита транзакции
],
],
config/database.php — отдельное Redis подключение для очередей:
'queue' => [
'host' => env('REDIS_QUEUE_HOST', '127.0.0.1'),
'password' => env('REDIS_QUEUE_PASSWORD'),
'port' => env('REDIS_QUEUE_PORT', '6379'),
'database' => env('REDIS_QUEUE_DB', '2'),
'read_timeout' => 60,
],
Создание Job:
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendOrderConfirmationEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public int $tries = 3;
public int $timeout = 60;
public int $backoff = 30; // Секунды между повторными попытками
public function __construct(
private readonly int $orderId
) {}
public function handle(OrderRepository $orders, Mailer $mailer): void
{
$order = $orders->findWithItems($this->orderId);
$mailer->to($order->customer_email)
->send(new OrderConfirmation($order));
}
public function failed(\Throwable $exception): void
{
// Уведомить команду о провале
\Log::error('Order confirmation email failed', [
'order_id' => $this->orderId,
'error' => $exception->getMessage(),
]);
}
}
Постановка в очередь:
// Немедленно
SendOrderConfirmationEmail::dispatch($order->id);
// С задержкой (5 минут)
SendOrderConfirmationEmail::dispatch($order->id)->delay(now()->addMinutes(5));
// В конкретную очередь
SendOrderConfirmationEmail::dispatch($order->id)->onQueue('emails');
// Цепочка задач (выполняются последовательно)
ProcessImage::withChain([
new GenerateThumbnails($imageId),
new SendNotification($userId),
])->dispatch($imageId);
Запуск воркеров
# Один воркер, очередь 'default'
php artisan queue:work redis --queue=default --sleep=3 --tries=3
# Несколько очередей с приоритетами
php artisan queue:work redis --queue=critical,high,default --timeout=60
# Daemon режим (не перезапускает PHP между задачами — быстрее)
php artisan queue:work --daemon
# После деплоя нового кода — перезапустить воркеры
php artisan queue:restart
Supervisor для управления воркерами в продакшене:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/worker.log
stopwaitsecs=3600
numprocs=4 — четыре параллельных воркера. Для IO-интенсивных задач (email, API calls) можно больше. Для CPU-интенсивных (обработка изображений) — не больше числа ядер.
Мониторинг очередей
Laravel Horizon — официальный dashboard для мониторинга Redis Queue:
composer require laravel/horizon
php artisan horizon:install
config/horizon.php:
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
'queue' => ['critical', 'default', 'emails'],
'balance' => 'auto', // Автоматически распределять процессы по очередям
'minProcesses' => 1,
'tries' => 3,
'timeout' => 60,
],
],
],
Запуск:
php artisan horizon
Horizon автоматически управляет числом воркеров под нагрузку и отображает метрики: throughput, failed jobs, wait time.
Failed Jobs
Провальные задачи сохраняются в Redis (или БД). Просмотр и повторный запуск:
# Список провальных задач
php artisan queue:failed
# Повторить конкретную задачу
php artisan queue:retry 5
# Повторить все провальные
php artisan queue:retry all
# Удалить провальные
php artisan queue:flush
Сроки
Базовая настройка Laravel Queue с Redis и Supervisor — 1 рабочий день. Добавление Horizon с мониторингом и настройкой auto-scaling — ещё полдня. Реализация сложных цепочек задач с retry-логикой и алертами на failed jobs — 1–2 дня.







