Laravel Queue队列

2026-09-04 51

Laravel 项目中,有些任务不适合让用户一直等待。例如发送邮件、生成报表、处理图片、同步第三方接口、批量导入数据等。如果这些操作都在请求中同步执行,页面响应会变慢,甚至可能因为接口超时导致用户提交失败。Laravel Queue 可以把耗时任务放到后台异步处理,让前台请求更快返回。

一、Laravel Queue 是什么

Laravel Queue 是 Laravel 提供的队列系统。它可以把需要稍后执行的任务放入队列,再由后台 worker 进程逐个处理。

简单理解:

  1. 用户提交请求;
  2. 控制器把耗时任务放入队列;
  3. 页面快速返回结果;
  4. 后台队列进程继续执行任务;
  5. 任务失败时可以记录并重试。

队列适合处理不需要立刻完成、但必须可靠执行的任务。

常见使用场景包括:

  1. 发送注册邮件;
  2. 发送订单通知;
  3. 生成 PDF 或报表;
  4. 处理上传图片;
  5. 同步第三方接口;
  6. 批量导入 Excel;
  7. 推送消息通知;
  8. 清理日志或缓存。

二、Laravel 支持哪些队列驱动

Laravel 支持多种队列连接方式,包括 database、Redis、Amazon SQS 等。开发测试时可以先使用 database,生产环境常见选择是 Redis。

队列配置文件位于:

config/queue.php

环境变量中可以设置默认队列驱动:

QUEUE_CONNECTION=database

如果使用 Redis:

QUEUE_CONNECTION=redis

如果项目还没有安装和配置 Redis,可以先用 database 驱动完成队列入门测试。

三、使用 database 队列驱动

使用 database 队列前,需要创建队列表。

执行命令:

php artisan queue:table
php artisan migrate

执行后,数据库中会生成 jobs 表,用来保存待执行任务。

如果需要记录失败任务,可以创建失败任务表:

php artisan queue:failed-table
php artisan migrate

之后失败任务会记录到 failed_jobs 表中,方便查看和重试。

四、创建 Job 任务类

使用 Artisan 创建一个任务类:

php artisan make:job SendWelcomeEmail

生成文件位置通常是:

app/Jobs/SendWelcomeEmail.php

基础示例:

<?php

namespace App\Jobs;

use App\Models\User;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public User $user
    ) {
    }

    public function handle(): void
    {
        Mail::to($this->user->email)
            ->send(new WelcomeMail($this->user));
    }
}

实现 ShouldQueue 接口后,这个任务就会进入队列异步执行。如果没有实现该接口,任务可能会被同步执行。

handle() 方法中写真正需要执行的逻辑。

五、分发队列任务

在控制器或服务类中,可以使用 dispatch() 分发任务:

use App\Jobs\SendWelcomeEmail;
use App\Models\User;

public function store()
{
    $user = User::create([
        'name' => request('name'),
        'email' => request('email'),
        'password' => bcrypt(request('password')),
    ]);

    SendWelcomeEmail::dispatch($user);

    return response()->json([
        'message' => '注册成功',
    ]);
}

这样用户注册成功后,接口可以先返回响应,邮件发送任务交给后台队列处理。

也可以延迟执行任务:

SendWelcomeEmail::dispatch($user)->delay(now()->addMinutes(5));

延迟任务适合提醒、通知、定时检查等场景。

六、运行队列 worker

任务进入队列后,需要启动 worker 才会真正执行。

执行命令:

php artisan queue:work

如果只想处理指定队列,可以使用:

php artisan queue:work --queue=emails

在本地开发时,可以直接在终端运行。生产环境中,队列 worker 应由 Supervisor、systemd 或容器进程管理工具托管,保证异常退出后能够自动重启。

七、指定队列名称

不同类型任务可以放入不同队列。例如邮件任务放入 emails 队列,报表任务放入 reports 队列。

分发任务时指定队列:

SendWelcomeEmail::dispatch($user)->onQueue('emails');

运行对应 worker:

php artisan queue:work --queue=emails

如果有多个队列,可以设置优先级:

php artisan queue:work --queue=high,default,emails

Laravel 会优先处理排在前面的队列。

八、配置任务重试次数

任务可能因为网络波动、邮件服务异常、第三方接口超时而失败。可以在 Job 类中设置最大尝试次数:

class SendWelcomeEmail implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;

    public function handle(): void
    {
        // 执行任务
    }
}

也可以设置超时时间:

public int $timeout = 120;

这表示任务执行超过 120 秒后会被视为超时。

如果需要控制重试间隔,可以定义 backoff()

public function backoff(): array
{
    return [60, 300, 600];
}

表示第一次失败后 60 秒重试,第二次失败后 300 秒重试,第三次失败后 600 秒重试。

九、处理失败任务

如果任务多次重试后仍然失败,会进入失败任务记录。

查看失败任务:

php artisan queue:failed

重试所有失败任务:

php artisan queue:retry all

重试指定失败任务:

php artisan queue:retry 1

删除所有失败任务:

php artisan queue:flush

如果想在任务最终失败时执行自定义逻辑,可以在 Job 中添加 failed() 方法:

use Throwable;

public function failed(Throwable $exception): void
{
    report($exception);
}

这里可以记录日志、发送告警或更新业务状态。

十、避免队列任务中的常见问题

队列任务看起来简单,但实际项目中很容易遇到一些坑。

1、不要传递过大的数据

Job 构造方法中不要传入大数组、大文件内容或复杂对象。更推荐传模型 ID,再在 handle() 中重新查询。

SendWelcomeEmail::dispatch($user->id);

然后在 Job 中查询:

$user = User::findOrFail($this->userId);

这样可以减少队列负担,也降低序列化异常风险。

2、任务要能重复执行

队列任务可能因为失败重试而执行多次,所以任务逻辑要尽量具备幂等性。

例如发送优惠券、扣减库存、调用支付接口等操作,必须避免重复执行带来业务错误。可以通过状态字段、唯一记录或业务流水号进行保护。

3、外部接口必须设置超时

如果队列任务调用第三方 API,一定要设置超时和失败处理。否则 worker 可能长时间卡住,影响后续任务执行。

4、修改代码后要重启 worker

生产环境中,queue:work 是长驻进程。代码更新后,旧 worker 不会自动加载新代码,需要执行:

php artisan queue:restart

该命令会让 worker 在当前任务处理完成后平滑重启。

十一、生产环境如何守护队列进程

生产环境不建议手动开一个终端运行 php artisan queue:work。一旦终端关闭或进程异常退出,队列任务就会停止处理。

常见做法是使用 Supervisor 管理 worker。示例配置:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/example.com/artisan queue:work redis --sleep=3 --tries=3 --timeout=120
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/worker.log
stopwaitsecs=3600

配置完成后启动 Supervisor,即可让队列 worker 常驻运行。不同服务器环境路径和用户不同,需要按实际项目调整。

十二、队列和定时任务有什么区别

队列用于异步处理任务,定时任务用于按时间周期触发任务。

例如:

  • 用户注册后发送邮件,适合队列;
  • 每天凌晨生成报表,适合定时任务;
  • 报表生成过程耗时较长,可以由定时任务触发队列任务;
  • 批量同步接口,可以定时触发,再交给队列分批处理。

两者经常配合使用,不是替代关系。

十三、常见问题

Laravel 队列任务为什么没有执行?

常见原因是没有启动 queue:work,或者 QUEUE_CONNECTION 配置不正确。使用 database 驱动时,还要确认 jobs 表已经创建。

修改 Job 代码后为什么不生效?

生产环境的队列 worker 是长驻进程,代码更新后需要执行 php artisan queue:restart,让 worker 重新加载代码。

队列适合处理所有耗时任务吗?

大部分耗时任务适合放入队列,但任务本身要有清晰的失败处理和重试机制。涉及支付、库存和外部系统写入时,还要做好幂等设计。

database 和 Redis 队列怎么选?

入门和小项目可以使用 database,配置简单。任务量较大、并发更高或需要更好性能时,Redis 更合适。

队列任务失败后会自动重试吗?

可以自动重试,但需要配置尝试次数、超时时间和失败任务表。最终失败后,可以通过 queue:failed 查看,并使用 queue:retry 重新执行。

  • 广告合作

  • QQ群号:4114653

温馨提示:
1、本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。邮箱:2942802716#qq.com(#改为@)。 2、本站原创内容未经允许不得转裁,转载请注明出处“站长百科”和原文地址。