laravel中队列使用

news/2024/11/30 4:46:58/

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"><?phpnamespace 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());}
}

提交任务

控制器中提交

php">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!']);
}

注意事项

任务可能会因为失败被多次执行,因此任务逻辑需要设计为幂等。例如,为每个任务分配唯一标识,避免重复处理。


http://www.ppmy.cn/news/1551101.html

相关文章

C#里怎么样使用LINQ的let关键字实现查询?

C#里怎么样使用LINQ的let关键字实现查询? 在C#中,let关键字是用来在查询表达式中声明一个范围变量的。范围变量是在迭代过程中保存查询产生的序列中的元素的临时变量。 以下是一个使用let关键字的示例代码: /** C# Program to Implement Let Condition using LINQ*/ usi…

ubuntu客户端使用飞牛云的smb服务端共享,和ftp记录

ubuntu smb客服端链接 在Ubuntu上设置SMB客户端连接到SMB服务器&#xff0c;你可以使用smbclient工具或者挂载共享目录。以下是使用smbclient和挂载的简单示例&#xff1a; 使用smbclient连接SMB服务器 安装smbclient&#xff1a; sudo apt-update sudo apt-get install smbcli…

Vue Promise的使用,界面使用异步线程循环执行方法(模拟线程)

目录 1.定义开始和退出标识 2.定义开始方法--异步 3.定义循环方法&#xff0c;以及控制规则 4.定义业务方法 1.定义开始和退出标识 为的是能控制开始和结束&#xff0c;记得销毁时要结束循环&#xff0c;否则方法会一直被执行 data() {return {isrunning: false, // 轮询…

第四十二篇 EfficientNet:重新思考卷积神经网络的模型缩放

文章目录 摘要1、简介2、相关工作3、复合模型缩放3.1、 问题公式化3.2、扩展维度3.3、复合比例 4、EfficientNet架构5、实验5.1、扩展MobileNets和ResNets5.2、EfficientNet的ImageNet结果5.3、EfficientNet的迁移学习结果 6、讨论7、结论 摘要 卷积神经网络(ConvNets)通常在固…

element-ui 中el-calendar 日历插件获取显示的第一天和最后一天【原创】

需要获取el-calendar 日历组件上的第1天和最后一天。可以通过document.querySelector()方法进行获取dom元素中的值&#xff0c;这样避免计算问题。 获取的过程中主要有两个难点&#xff0c;第1个是处理上1月和下1月的数据&#xff0c;第2个是跨年的数据。 直接贴代码&#xff…

基于Matlab SIR模型的传染病动态模拟与扩展研究

SIR模型作为流行病学领域的经典模型&#xff0c;在研究传染病传播规律和动态变化方面发挥了重要作用&#xff0c;为分析疾病在特定人群中的传播趋势提供了理论基础和工具支持。然而&#xff0c;传统的SIR模型假设人口总数不变且免疫力永久有效&#xff0c;在面对实际复杂的疫情…

Python基础学习-12匿名函数lambda和map、filter

目录 1、匿名函数&#xff1a; lambda 2、Lambda的参数类型 3、map、 filter 4、本节总结 1、匿名函数&#xff1a; lambda 1&#xff09;语法&#xff1a; lambda arg1, arg2, …, argN : expression using arg 2&#xff09; lambda是一个表达式&#xff0c;而不是一个语…

Vue3自定义组件里数据双向绑定的实现

一、组合式API里的写法 1、defineModel defineModel()返回的值是一个ref&#xff0c;它可以像其他ref一样被访问以及修改&#xff0c;不过它能起到在父组件和当前变量之间的双向绑定的作用。 它的.value和父组件的v-model的值一起更新当它的子组件变更了&#xff0c;会触发父…