在现代 Web 应用开发中,有些操作需要耗费较长时间——发送邮件、生成报表、处理图片、调用第三方 API。如果让用户同步等待这些操作完成,体验会非常糟糕。Laravel 的队列系统正是为解决这类问题而生,它将耗时任务"推迟"到后台处理,让 HTTP 响应瞬间返回。
本文将从基础概念入手,逐步深入到 Laravel 队列系统的高级用法,包括驱动选择、任务编写、队列监控与调优,帮助你构建高性能的异步处理流水线。
Laravel 队列的核心思想非常简单:将需要执行的任务序列化后存入"队列"中,然后由独立的"工作者"(Worker)进程逐个取出并执行。
HTTP 请求 → 将任务推入队列 → 立即返回响应
↓
后台 Worker → 从队列取出任务 → 执行实际逻辑
这种架构带来的好处是显而易见的:
Laravel 支持多种队列驱动,config/queue.php 是配置入口。
最简单的持久化方案,无需额外服务:
php artisan queue:table php artisan migrate
jobs 表结构包含 id、queue(队列名)、payload(序列化的任务)、attempts(重试次数)、reserved_at(保留时间)、available_at(可执行时间)、created_at 等字段。
性能远超数据库驱动,适合高并发场景:
// config/queue.php
'default' => env('QUEUE_CONNECTION', 'redis'),
'connections' => [
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => null,
'after_commit' => true,
],
],
需要安装 predis/predis 或 phpredis 扩展,并在 .env 中配置:
QUEUE_CONNECTION=redis REDIS_QUEUE=default
任务是队列的最小执行单元,通常继承 ShouldQueue 接口。
php artisan make:job ProcessOrder
生成的类位于 app/Jobs/ProcessOrder.php:
<?php
namespace App\Jobs;
use App\Models\Order;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ProcessOrder implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, SerializesModels;
public function __construct(
public Order $order
) {}
public function handle(): void
{
// 执行订单处理逻辑
$this->order->markAsProcessing();
// 调用物流 API
app(ShippingService::class)->createShipment($this->order);
// 发送通知
$this->order->user->notify(new OrderShipped($this->order));
}
}
在控制器或其他位置分发任务:
// 延迟执行
ProcessOrder::dispatch($order)->delay(now()->addMinutes(10));
// 指定队列
ProcessOrder::dispatch($order)->onQueue('high');
// 指定连接
ProcessOrder::dispatch($order)->onConnection('redis');
// 链式组合
ProcessOrder::dispatch($order)
->onQueue('high')
->onConnection('redis')
->delay(now()->addMinutes(5));
Laravel 支持按顺序执行任务链:
Bus::chain([
new ProcessOrder($order),
new SendOrderConfirmation($order),
new UpdateInventory($order),
])->dispatch();
当需要批次处理大量数据时,使用批处理更合适:
$batch = Bus::batch([
new ProcessOrder($order1),
new ProcessOrder($order2),
// ...
])->then(function (Batch $batch) {
// 所有任务完成后的回调
})->catch(function (Batch $batch, Throwable $e) {
// 有任务失败时的回调
})->finally(function (Batch $batch) {
// 无论成功失败都会执行
})->dispatch();
return $batch->id;
通过 $batch->id 可在前端实时查询处理进度:
$batch = Bus::findBatch($batchId);
return response()->json([
'progress' => $batch->progress(),
'finished' => $batch->finished(),
'failed' => $batch->failedJobs,
]);
# 启动默认队列的 Worker php artisan queue:work # 指定队列和连接 php artisan queue:work redis --queue=high,default # 指定每次最多处理 3 个任务后退出(用于避免内存泄漏) php artisan queue:work --max-jobs=1000
使用 Supervisor 守护 Worker 进程,确保其崩溃后自动重启。安装 Supervisor 后创建配置 /etc/supervisor/conf.d/laravel-worker.conf:
[program:laravel-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/html/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 autostart=true autorestart=true stopasgroup=true killasgroup=true user=www-data numprocs=8 redirect_stderr=true stdout_logfile=/var/www/html/storage/logs/worker.log stopwaitsecs=3600
关键参数说明:
numprocs=8:启动 8 个 Worker 进程并行处理--tries=3:每个任务最多重试 3 次--sleep=3:无任务时休眠 3 秒--max-time=3600:Worker 运行 1 小时后自动重启,防止内存泄漏执行 failed_jobs 表迁移:
php artisan queue:failed-table php artisan migrate
当任务超出重试次数后,会写入 failed_jobs 表。可以在任务类中定义 failed 方法:
public function failed(\Throwable $e): void
{
Log::error('订单处理失败', [
'order_id' => $this->order->id,
'error' => $e->getMessage(),
]);
$this->order->markAsFailed();
Notification::send(admin, new JobFailedNotification($this->order));
}
# 查看所有失败任务 php artisan queue:failed # 重试指定 ID 的任务 php artisan queue:retry 5 # 重试所有失败任务 php artisan queue:retry all # 删除失败任务 php artisan queue:forget 5 # 清空所有失败任务 php artisan queue:flush
避免调用外部 API 时触发限流:
public function handle(): void
{
// 每秒最多处理 10 个任务
Redis::throttle('api-calls')->allow(10)->every(1)->then(
function () {
// 执行业务逻辑
$this->syncProducts();
},
function () {
// 释放回队列,5 秒后重试
$this->release(5);
}
);
}
合理划分队列级别,确保重要任务优先处理:
// 高优先级队列:订单支付、即时通知
ProcessPayment::dispatch($payment)->onQueue('high');
// 默认队列:邮件发送、数据处理
SendInvoice::dispatch($invoice)->onQueue('default');
// 低优先级队列:日志清理、报表生成
GenerateReport::dispatch($report)->onQueue('low');
Worker 启动时按优先级处理:
php artisan queue:work --queue=high,default,low
Worker 会优先消费 high 队列,该队列为空后再处理 default,最后处理 low。
Laravel 为队列系统提供了丰富的事件,可以在 AppServiceProvider 中注册监听器:
public function boot(): void
{
Queue::before(function (JobProcessing $event) {
Log::info('任务开始处理', [
'job' => $event->job->resolveName(),
'queue' => $event->job->getQueue(),
]);
});
Queue::after(function (JobProcessed $event) {
// 任务处理完成
});
Queue::failing(function (JobFailed $event) {
// 任务失败告警
Alert::critical("任务失败: {$event->job->resolveName()}");
});
Queue::looping(function () {
// 每次循环时的回调,可用于检查内存使用
if (memory_get_usage() > 100 * 1024 * 1024) {
exit; // Worker 自动退出,Supervisor 会重启
}
});
}
如果使用 Redis 驱动,强烈建议安装 Laravel Horizon——官方的队列监控面板:
composer require laravel/horizon php artisan horizon:install
配置 config/horizon.php 后,启动 Horizon 代替 queue:work:
php artisan horizon
Horizon 提供了一套美观的 Web 面板,实时展示队列吞吐量、等待时间、失败任务、Worker 状态等指标,还包括任务指标的趋势图表,非常适合生产环境的日常运维。
SerializesModels Trait 会在序列化时只保留模型的主键,反序列化时从数据库重新查询。这意味任务入列后如果模型数据发生变化,Worker 读取的是最新数据,而不是入列时的数据。如果需要快照数据,手动传递所需字段:
public function __construct(
public int $orderId,
public array $orderData, // 手动传递数据快照
) {}
handle 方法中开启的数据库连接、HTTP 客户端等资源要及时释放。使用 once 或 finally 保证资源清理。
防止同一任务被重复入列,利用 Redis 实现唯一队列:
public function handle(): void
{
$lock = Cache::lock("process-order-{$this->order->id}", 10);
if (!$lock->get()) {
$this->release(10);
return;
}
try {
// 执行处理逻辑
} finally {
$lock->release();
}
}
无论任务执行一次还是多次,结果应该一致。例如发送邮件前先检查是否已发送过,扣减库存时使用数据库原子操作。
Supervisor 配置 stopwaitsecs=3600 确保 Worker 处理完当前任务后才退出,避免任务中断丢失。同时配合 queue:work --timeout=0 让 Worker 自己管理超时。
Laravel 队列系统是构建高性能 Web 应用的基石。从最简单的数据库驱动到生产环境的 Redis + Horizon 组合,Laravel 提供了从入门到工业级的完整方案。
以一个典型的电商系统为例:
ProcessOrder 入列 high 队列 → 响应返回"订单处理中"这套流程将原本需要 3-5 秒的同步操作压缩到 50ms 以内完成响应,显著提升了用户体验和系统吞吐量。
掌握队列系统,意味着你能够构建一种"异步优先"的架构思维——不再被同步阻塞束缚,而是以事件驱动的方式编排业务流程。这正是现代后端开发的核心能力之一。