Laravel 提供了强大的队列系统,允许开发者将耗时任务推送到后台执行,从而提升系统性能和用户体验。本文将从基本使用到深入解析,结合单进程队列的特点,完整地介绍 Laravel 队列的使用。
队列的作用和场景*
在 Web 开发中,一些任务会占用较长时间,例如:
• 发送邮件:用户注册成功后需要发送欢迎邮件。
• 生成报表:复杂报表需要长时间计算和数据处理。
• 推送通知:需要通过第三方服务发送推送。
将这些任务放在 HTTP 请求中会导致用户等待时间过长,影响体验。Laravel 的队列系统将这些任务推到后台运行,前端只需快速响应即可。
队列的安装与配置**
1. 配置队列驱动
Laravel 支持多种队列驱动,如 database、Redis、beanstalkd 等。默认驱动为 sync,即同步执行任务。修改 .env 文件以指定驱动:
QUEUE_CONNECTION=redis
安装与配置 Redis
2. 安装 PHP Redis 扩展
通过 Composer 安装 PHP 的 Redis 扩展:
composer require predis/predis
或者安装官方扩展:
# Ubuntu
sudo apt install php-redis
# CentOS
sudo yum install php-redis
Laravel 配置 Redis 队列
1. 修改队列连接设置
在 .env 文件中将队列驱动改为 redis:
QUEUE_CONNECTION=redis
2. 配置 Redis 连接
Laravel 默认使用 config/database.php 中的 Redis 配置,无需额外改动。默认配置如下:
'redis' => [
'client' => 'phpredis', // 或 'predis',根据安装的扩展选择
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
'queue' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_QUEUE_DB', 1), // 队列可以使用独立的 Redis 数据库
],
],
确保 .env 中的 Redis 配置正确:
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0
REDIS_QUEUE_DB=1
运行 Redis 队列
. 启动队列进程
单进程运行解析:
php artisan queue:work redis --queue=emails --tries=3 --timeout=60
• redis:指定使用 Redis 作为队列驱动。
• --tries=3:失败重试次数。默认:无限次
• --timeout=60:任务最大执行时间。任务执行时间超过 60 秒会超时,标记为失败
–queue 指定队列名称 (例如,任务被分配到不同队列(emails, notifications),可以选择只处理 emails 队列的任务。)
–memory 限制队列进程的内存使用(单位:MB)
–sleep 设置没有任务时,进程的等待时间(单位:秒)(php artisan queue:work redis --sleep=3 如果队列为空,进程会等待 3 秒再尝试获取新任务。)
任务会按照队列的先进先出顺序(FIFO)串行执行。假设队列中有任务 A、B、C:
1. 任务 A 被处理完成后,任务 B 才会开始。
2. 任务 B 完成后,任务 C 开始。
3. 当前任务完成后,工作进程会继续监听队列中新的任务。
这种运行方式仍然是异步的,任务不会阻塞 HTTP 请求响应,而是后台依次执行。
多进程运行
php artisan queue:work --tries=3 --timeout=60 &
php artisan queue:work --tries=3 --timeout=60 &
php artisan queue:work --tries=3 --timeout=60 &
php artisan queue:work --tries=3 --timeout=60 &
如果需要给队列取名
–queue=emails
php artisan queue:work redis --queue=emails1 --tries=3 --timeout=60 &
php artisan queue:work redis --queue=emails2 --tries=3 --timeout=60 &
php artisan queue:work redis --queue=emails3 --tries=3 --timeout=60 &
php artisan queue:work redis --queue=emails4 --tries=3 --timeout=60 &
php artisan queue:work redis --queue=emails5 --tries=3 --timeout=60 &
如果启动多个队列工作进程,队列中的任务将被多个进程同时处理。例如:
• 任务 A 分配给第一个进程。
• 任务 B 分配给第二个进程。
• 如果有更多任务,它们会被动态分配给空闲进程。
这种方式提高了队列处理的并发能力,适合需要高吞吐量的场景。
优化与最佳实践**
1. 使用 Supervisor 管理队列
在生产环境中,使用 Supervisor 可以确保队列进程始终运行。
配置 Supervisor:
在 /etc/supervisor/conf.d/laravel-worker.conf 中添加以下内容:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /path/to/project/artisan queue:work --tries=3 --timeout=60
autostart=true
autorestart=true
numprocs=4
redirect_stderr=true
stdout_logfile=/path/to/project/worker.log
启动 Supervisor:
# 重新加载 Supervisor 配置
sudo supervisorctl reread
# 更新 Supervisor 进程
sudo supervisorctl update
# 启动所有 laravel-worker 进程
sudo supervisorctl start laravel-worker:*
numprocs: 启动4个队列进程
2 验证运行状态
运行以下命令查看进程状态:
sudo supervisorctl status
输出示例:
laravel-worker:laravel-worker_00 RUNNING pid 1234, uptime 0:01:00
laravel-worker:laravel-worker_01 RUNNING pid 1235, uptime 0:01:00
laravel-worker:laravel-worker_02 RUNNING pid 1236, uptime 0:01:00
laravel-worker:laravel-worker_03 RUNNING pid 1237, uptime 0:01:00
3. 多队列分配
如果需要针对不同的队列启动多个进程,例如分别处理 emails 队列和 notifications 队列,可以配置多个程序块。
[program:emails-queue]
command=php /path/to/project/artisan queue:work redis --queue=emails --tries=3 --timeout=60
autostart=true
autorestart=true
numprocs=2
redirect_stderr=true
stdout_logfile=/path/to/project/logs/emails_queue.log
[program:notifications-queue]
command=php /path/to/project/artisan queue:work redis --queue=notifications --tries=3 --timeout=60
autostart=true
autorestart=true
numprocs=2
redirect_stderr=true
stdout_logfile=/path/to/project/logs/notifications_queue.log
[program:reports-queue]
command=php /path/to/project/artisan queue:work redis --queue=reports --tries=3 --timeout=60
autostart=true
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/path/to/project/logs/reports_queue.log
此配置会分别启动两个队列,每个队列都有两个工作进程。
使用
创建与调度任务
php artisan make:job SendEmailJob
生成的类位于 app/Jobs/SendEmailJob.php,代码如下:
<?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 SendEmailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $email;
public function __construct($email)
{
$this->email = $email;
}
public function handle()
{
// 模拟邮件发送逻辑
\Mail::to($this->email)->send(new \App\Mail\WelcomeMail());
}
}
提交任务
控制器中提交
use App\Jobs\SendEmailJob;
public function sendEmail(Request $request)
{
$email = $request->input('email');
// 将任务加入队列,不指定队列
SendEmailJob::dispatch($email);
// --------------------------- 指定队列
// 第一个任务
SendEmailJob::dispatch($email)->onQueue('emails');
// 第二个任务
SendNotificationJob::dispatch($email)->onQueue('notifications');
return response()->json(['message' => 'Email is being sent!']);
}
注意事项
任务可能会因为失败被多次执行,因此任务逻辑需要设计为幂等。例如,为每个任务分配唯一标识,避免重复处理。