Драйверы очередей
Что такое драйвер
Драйвер — где хранится очередь и как обрабатывается.
В config/queue.php:
'default' => env('QUEUE_CONNECTION', 'database'),
Sync
Для dev / тестов:
QUEUE_CONNECTION=sync
Что делает: выполняет Job сразу, синхронно.
Плюсы:
- Просто — ничего ставить не надо.
- Отладка — сразу видно результат.
Минусы:
- Блокирует HTTP.
- Нет retry.
- Нет параллельности.
Когда: локальная разработка, тесты.
Database
Очередь в БД:
QUEUE_CONNECTION=database
Миграция:
php artisan queue:table
php artisan queue:failed-table
php artisan migrate
Что создаёт:
jobs— таблица задач.job_batches— группы.failed_jobs— провалы.
Как работает:
dispatchвставляет строку вjobs.- Worker читает и удаляет.
- При ошибке →
failed_jobs.
Плюсы:
- Просто — не нужен Redis.
- Надёжно — БД с бэкапами.
- Видно — можно смотреть SQL.
Минусы:
- Медленно на больших объёмах.
- Блокировки при конкурентных воркерах.
- Нагрузка на БД.
Когда: маленькие проекты, нет Redis.
Redis
Рекомендуемый:
QUEUE_CONNECTION=redis
Что нужно:
composer require predis/predis
# или ext-redis
config/database.php:
'redis' => [
'client' => 'phpredis', // или predis
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', 6379),
'password' => env('REDIS_PASSWORD'),
'database' => 0,
],
],
Как работает:
- Job — в список Redis.
- Worker забирает.
BRPOPLPUSH— атомарно.
Плюсы:
- Скорость — миллисекунды.
- Horizon — UI и метрики.
- Масштабирование — миллионы задач.
- Отложенные — через sorted sets.
Минусы:
- Нужен Redis.
- RAM — если задач много.
- Не персистится по умолчанию (можно настроить).
Когда: продакшен, средние и крупные проекты.
Laravel Horizon
Дашборд для Redis-очередей.
composer require laravel/horizon
php artisan horizon:install
Запуск:
php artisan horizon
Что даёт:
- Метрики — сколько задач, throughput.
- Failed jobs — retry из UI.
- Мониторинг — воркеры, очереди.
- Auto-balance — распределение.
URL: /horizon.
В проде: supervisor + horizon.
Amazon SQS
Managed от AWS:
QUEUE_CONNECTION=sqs
AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=xxx
AWS_DEFAULT_REGION=us-east-1
SQS_PREFIX=https://sqs.us-east-1.amazonaws.com/your-account-id
SQS_QUEUE=default
Плюсы:
- Managed — нет своей инфры.
- Надёжно — AWS SLA.
- Бесплатно — миллион запросов в месяц.
Минусы:
- Латентность — 10-100 мс.
- Vendor lock-in.
Когда: AWS-инфраструктура.
Beanstalkd
Лёгкий брокер:
sudo apt install beanstalkd
QUEUE_CONNECTION=beanstalkd
BEANSTALKD_QUEUE_HOST=localhost
Плюсы:
- Простой.
- Быстрый.
Минусы:
- Не персистентный (по умолчанию).
- Меньше инструментов.
Когда: редко — Redis или database лучше.
Сравнение
| Драйвер | Скорость | Надёжность | Простота |
|---|---|---|---|
| sync | Мгновенно | Нет retry | ⭐⭐⭐⭐⭐ |
| database | Медленно | Высокая | ⭐⭐⭐⭐⭐ |
| redis | Быстро | Средняя | ⭐⭐⭐⭐ |
| sqs | Средне | Высокая | ⭐⭐⭐ |
| beanstalkd | Быстро | Средняя | ⭐⭐ |
Приоритеты в конфиге
Несколько соединений:
'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
],
'redis-high' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => 'high',
],
],
Dispatch:
ProcessPodcast::dispatch($podcast)->onConnection('redis-high');
Конфиг queues
Внутри connection:
'redis' => [
'driver' => 'redis',
'queue' => 'default',
'retry_after' => 90, // сек до повтора
'block_for' => null, // ждать задачу
'after_commit' => true, // после commit транзакции
],
Что значит:
retry_after— если worker умер, через 90 сек задача вернётся.block_for— worker ждёт задачу, а не спит.after_commit— Job уйдёт только после commit БД-транзакции.
after_commit
Проблема:
DB::transaction(function () {
$user = User::create([...]);
SendWelcomeEmail::dispatch($user); // Job может выполниться до commit
});
// Job обработается, но юзера ещё нет в БД
Решение:
// config/queue.php
'after_commit' => true,
Что даёт: Job уйдёт только после успешного commit.
Фейки
В тестах:
use Illuminate\Support\Facades\Queue;
public function test_send_email_dispatched(): void
{
Queue::fake();
$this->post('/register', [...]);
Queue::assertPushed(SendWelcomeEmail::class);
Queue::assertPushed(SendWelcomeEmail::class, function ($job) {
return $job->user->email === 'test@example.com';
});
Queue::assertNotPushed(SomeOtherJob::class);
}
Что даёт: тесты без реальных задач.
Supervisor
Автозапуск воркеров:
/etc/supervisor/conf.d/laravel-worker.conf:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
user=www-data
autostart=true
autorestart=true
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/worker.log
stopwaitsecs=3600
Что даёт: 4 воркера, автозапуск.
Перезапуск после деплоя:
sudo supervisorctl restart laravel-worker:*
php artisan queue:restart
Масштабирование
Больше воркеров:
numprocs=10
Несколько серверов:
- Все воркеры смотрят в одну очередь.
- Redis / SQS шарит состояние.
Что даёт: горизонтальное масштабирование.
Практика: переключение драйверов
1. Dev:
QUEUE_CONNECTION=sync
2. Prod с database:
QUEUE_CONNECTION=database
3. Prod с Redis:
QUEUE_CONNECTION=redis
4. Тесты:
Queue::fake();
Частые ошибки
1. sync в проде:
Все задачи выполняются в HTTP-запросе
2. Database-очередь без миграции:
Base table or view not found: jobs
Решение: php artisan queue:table && migrate.
3. Redis без клиента:
Class 'Redis' not found
Решение: composer require predis/predis.
4. retry_after меньше timeout:
Одна задача выполняется дважды
Правило: retry_after > timeout.
5. Забыли after_commit:
Job не видит модель, которая ещё не в БД
Итог
sync— для dev.database— просто и надёжно.redis— для прода.sqs— если AWS.- Horizon — UI и метрики.
after_commit— для транзакций.- Supervisor — автозапуск.
retry_after > timeout— правило.
Дальше — Laravel Horizon.