Очереди в Laravel: основы
Что даёт Laravel
Встроенная система очередей:
- Драйверы: sync, database, redis, sqs, beanstalkd.
- Jobs — классы задач.
- Workers —
queue:work. - Failed jobs — таблица.
- Horizon — для Redis.
- Schedule — расписание.
- Batches — группы.
- Chains — цепочки.
Настройка
.env:
QUEUE_CONNECTION=redis
Варианты:
sync— сразу (dev).database— таблица.redis— Redis.sqs— AWS.beanstalkd.
config/queue.php — параметры.
Создание Job
php artisan make:job SendWelcomeEmail
Файл app/Jobs/SendWelcomeEmail.php:
namespace App\Jobs;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
public User $user,
) {}
public function handle(): void
{
Mail::to($this->user)->send(new WelcomeEmail($this->user));
}
}
Что здесь
Трейты:
Dispatchable—Job::dispatch().InteractsWithQueue— методыdelete,release.Queueable— настройки очереди.SerializesModels— сериализация моделей по ID.
Интерфейс ShouldQueue — задача идёт в очередь.
handle — что делать.
Dispatch
Через класс:
SendWelcomeEmail::dispatch($user);
Через helper:
dispatch(new SendWelcomeEmail($user));
Синхронно:
SendWelcomeEmail::dispatchSync($user);
С задержкой:
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5));
В конкретную очередь:
SendWelcomeEmail::dispatch($user)->onQueue('emails');
Через соединение:
SendWelcomeEmail::dispatch($user)->onConnection('redis');
Worker
Запуск:
php artisan queue:work
Что происходит:
- Слушает очередь.
- Забирает задачу.
- Десериализует.
- Вызывает
handle. - Удаляет из очереди при успехе.
- При ошибке —
failed_jobs.
Параметры:
php artisan queue:work --queue=high,default # приоритет
php artisan queue:work --tries=3 # 3 попытки
php artisan queue:work --timeout=60 # 60 сек
php artisan queue:work --sleep=3 # пауза
--queue=high,default — сначала high, потом default.
Retry
В Job:
class SendWelcomeEmail implements ShouldQueue
{
public int $tries = 3;
public int $timeout = 30;
public int $backoff = 60;
public function handle(): void { }
public function failed(\Throwable $e): void
{
Log::error('Email failed', [
'user_id' => $this->user->id,
'error' => $e->getMessage(),
]);
}
}
Разбор:
$tries— сколько раз.$timeout— секунд на попытку.$backoff— секунд между попытками.failed— что делать после всех попыток.
Массив backoff
Разные задержки:
public array $backoff = [10, 30, 60];
Попытка 1 → упала → 10 сек. Попытка 2 → упала → 30 сек. Попытка 3 → упала → failed.
Release — вернуть в очередь вручную
public function handle(): void
{
if ($this->rateLimited()) {
$this->release(60); // вернуть через 60 сек
return;
}
// ...
}
Что даёт: если внешний API ещё не готов — отложить.
События Job
Hooks:
// AppServiceProvider::boot()
use Illuminate\Support\Facades\Queue;
Queue::before(function (JobProcessing $event) {
// перед началом
});
Queue::after(function (JobProcessed $event) {
// после успеха
});
Queue::failing(function (JobFailed $event) {
// после провала
});
В Job:
public function middleware(): array
{
return [new RateLimited('emails')];
}
Middleware
Rate limited:
use Illuminate\Queue\Middleware\RateLimited;
public function middleware(): array
{
return [new RateLimited('emails')];
}
В AppServiceProvider:
RateLimiter::for('emails', function ($job) {
return Limit::perMinute(100);
});
Что даёт: не более 100 email в минуту.
Без перекрытия (WithoutOverlapping)
public function middleware(): array
{
return [(new WithoutOverlapping($this->user->id))->dontRelease()];
}
Что даёт: не запускать вторую задачу для того же юзера.
Failed jobs
Таблица failed_jobs:
php artisan queue:failed
php artisan queue:retry 5
php artisan queue:retry all
php artisan queue:forget 5
php artisan queue:flush
Миграция:
php artisan queue:failed-table
php artisan migrate
Практика: регистрация
Controller:
public function store(StoreUserRequest $request)
{
$user = User::create($request->validated());
SendWelcomeEmail::dispatch($user);
ResizeAvatar::dispatch($user);
NotifyAdmin::dispatch($user);
return response()->json($user, 201);
}
Jobs:
// SendWelcomeEmail
public function handle(): void
{
Mail::to($this->user)->send(new WelcomeEmail($this->user));
}
// ResizeAvatar
public function handle(): void
{
Image::load($this->user->avatar)->resize(200, 200)->save();
}
// NotifyAdmin
public function handle(): void
{
Slack::send("New user: {$this->user->email}");
}
Запуск воркера:
php artisan queue:work
Проверка:
curl -X POST http://localhost:8000/api/register \
-H 'Content-Type: application/json' \
-d '{"name":"Иван","email":"ivan@example.com","password":"secret"}'
Ответ мгновенный, письмо — в фоне.
Частые ошибки
1. Забыли ShouldQueue:
class SendEmail
{
// без интерфейса — выполняется синхронно
}
2. Воркер не запущен:
Jobs накапливаются в БД, не выполняются
Решение: php artisan queue:work.
3. Данные в Job не сериализуются:
public function __construct(public $callback) { }
// Closure не сериализуется
Решение: только скаляры, модели (через SerializesModels).
4. Не обрабатывать failed:
Задача упала — никто не знает
5. Забыли queue:restart после деплоя:
Воркеры работают со старым кодом
Итог
- Job — класс задачи.
ShouldQueue— в очередь.dispatch,dispatchSync— способы.- Worker —
queue:work. $tries,$timeout,$backoff— настройки.failed— при провале.failed_jobs— таблица.- Middleware —
RateLimited,WithoutOverlapping.
Дальше — Laravel Jobs: углублённо.