Налаштування 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. Рекомендується для серйозних production-систем.
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 для управління воркерами в production:
[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 дні.







