在 Web 应用开发中,某些操作(如发送邮件、生成报表、处理图片)耗时较长,如果在请求生命周期内同步执行,会严重影响用户体验。Laravel 队列系统提供了一套优雅的解决方案,允许我们将耗时任务推送到后台异步执行,从而显著提升应用响应速度。
本文将从队列驱动配置、任务创建与分发、延迟执行、任务链到队列监控,系统讲解 Laravel 队列的完整使用方案。
什么是队列
队列(Queue)是一种异步处理机制。简单来说,就是将任务先存起来,等到有可用 worker 时再取出来执行。在 Laravel 中,队列的核心流程如下:
- 创建任务(Job):定义需要异步执行的逻辑
- 分发任务(Dispatch):将任务推送到队列存储(如 Redis、数据库)
- 消费任务(Work):队列 worker 从存储中取出任务并执行
配置队列驱动
Laravel 支持多种队列驱动:
| 驱动 | 说明 | 适用场景 |
|---|---|---|
sync |
同步执行 | 本地开发调试 |
database |
数据表存储 | 小型项目,无 Redis 环境 |
redis |
Redis 存储 | 生产环境首选,性能优秀 |
beanstalkd |
Beanstalkd | 轻量级消息队列 |
sqs |
AWS SQS | 云原生部署 |
推荐生产环境使用 Redis 驱动,配置 .env:
QUEUE_CONNECTION=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
如果使用数据库驱动,需先执行迁移创建任务表:
php artisan queue:table
php artisan migrate
创建任务类
使用 Artisan 命令生成任务类:
php artisan make:job SendWelcomeEmail
生成的 app/Jobs/SendWelcomeEmail.php:
<?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;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeMail;
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(
public User $user
) {}
public function handle(): void
{
Mail::to($this->user->email)
->send(new WelcomeMail($this->user));
}
}
ShouldQueue 接口是队列任务的核心标识。任务类中的 handle() 方法包含实际执行业务逻辑。SerializesModels trait 会自动序列化/反序列化 Eloquent 模型,非常便利。
分发任务
在控制器或服务中,一行代码即可将任务推送到队列:
use App\Jobs\SendWelcomeEmail;
// 默认队列
SendWelcomeEmail::dispatch($user);
// 指定队列名称
SendWelcomeEmail::dispatch($user)->onQueue('emails');
// 延迟执行(10 分钟后)
SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(10));
延迟执行
延迟执行是队列的常用场景。例如,用户注册后不立即发送欢迎邮件,而是等待 5 分钟:
SendWelcomeEmail::dispatch($user)
->delay(now()->addMinutes(5));
或者指定具体时间点:
SendWelcomeEmail::dispatch($user)
->delay(now()->addHours(2));
任务链与批处理
任务链
任务链允许按顺序执行多个任务,前一个失败则后续停止:
use Illuminate\Support\Facades\Bus;
Bus::chain([
new OptimizeImage($image),
new GenerateThumbnail($image),
new NotifyUser($user),
])->dispatch();
任务批处理
Laravel 8+ 支持批量分发任务,可统一监控完成状态:
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
$batch = Bus::batch([
new ImportCsvRow($row1),
new ImportCsvRow($row2),
new ImportCsvRow($row3),
])->then(function (Batch $batch) {
// 所有任务成功完成
})->catch(function (Batch $batch, Throwable $e) {
// 首次失败时触发
})->dispatch();
队列 Worker
启动队列 worker 消费任务:
# 基础启动
php artisan queue:work
# 指定连接和队列
php artisan queue:work redis --queue=emails,default
# 处理一个任务后退出(适合 cron)
php artisan queue:work --once
# 不重启持续运行(生产环境)
php artisan queue:work --sleep=3 --tries=3
重要:开发环境使用 queue:listen(修改代码自动生效),生产环境使用 queue:work(启动后常驻内存,性能更高)。
Supervisor 配置
生产环境建议使用 Supervisor 守护队列进程:
[program:snowmanblog-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /www/wwwroot/snowmanblog/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/snowmanblog-worker.log
失败任务处理
任务执行失败时,Laravel 会自动记录到 failed_jobs 表。查看失败任务:
php artisan queue:failed
重试指定失败任务:
php artisan queue:retry 1
重试所有失败任务:
php artisan queue:retry all
在任务类中自定义失败逻辑:
public function failed(\Throwable $exception): void
{
\Log::error('发送欢迎邮件失败', [
'user_id' => $this->user->id,
'error' => $exception->getMessage(),
]);
}
最佳实践
- 保持任务轻量:避免在
handle()中执行大量复杂逻辑,应拆分为多个小任务 - 合理设置超时:在任务类中定义
$timeout属性,防止长时间挂起 - 幂等性设计:任务可能被重试,确保多次执行不会产生副作用
- 监控队列长度:定期检查队列积压情况,及时调整 worker 数量
- 区分队列优先级:将邮件、通知等分为不同队列,优先处理核心任务
总结
Laravel 队列系统为异步处理提供了开箱即用的解决方案。从简单的邮件发送,到复杂的批量数据处理,队列都能游刃有余。合理使用队列不仅能提升用户体验,还能有效削峰填谷,保障系统在高并发场景下的稳定性。
如果你的项目尚未引入队列,不妨从发送邮件或生成报表这类非实时任务开始尝试,逐步感受异步编程带来的性能提升。