laravel 队列

最近在学习laravel框架,突然发现我似乎从未发布过关于队列消息方面的文章,今天结合laravel框架来说说队列消息方面的内容。

队列消息是什么?

消息(Message)是指在应用之间传送的数据,消息可以非常简单,比如只包含文本字符串,也可以更复杂,可能包含嵌入对象。
消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,有消息系统来确保信息的可靠专递,消息发布者只管把消息发布到MQ中而不管谁来取,消息使用者只管从MQ中取消息而不管谁发布的,这样发布者和使用者都不用知道对方的存在。

队列消息可以做什么?

增强web响应,将不需要立即响应的消息交给后台任务来执行
举个例子,一个用户A完成了注册操作,注册成功后我们需要发一份welcome邮件给用户,传统方案是采用同步执行发送(即是在后台业务程序处理中将邮件发送出去,发送出去后才返回消息给用户)。采用队列消息后,我们将发送邮件的工作交给队列去执行,而先把响应内容返回给用户,相关的邮件通知后续会由队列任务的执行而发送(队列任务是一个进程,它将一直在后台执行)

将复杂耗时的任务交由后台任务处理,从而快速给用户响应

laravel 队列消息的使用

首先需要配置队列消息,我采用的是依赖redis来存储队列消息,所以你在使用队列消息的时候最好先把laravel的redis配置先配置好

话不多说,上代码
1、要实现将一个任务放置至redis中,需要类实现ShouldQueue接口,并在类方法中定义handle方法,laravel 会自动扫描这个类是否实现了ShouldQueue接口,从而将类的实例放置进入redis。

<?php

namespace App\Jobs;

use App\Exceptions\AppException;
use App\Exceptions\Ecode;
use App\Models\User;
use Illuminate\Bus\Batchable;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class UserVersionJob implements ShouldQueue
{
    //队列特性trait
    use Batchable,Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $userORM;

    /**
     * Create a new job instance.
     * @param User  $userORM 一个用户ORM模型对象
     * @return void
     */
    public function __construct(User $userORM)
    {
        $this->userORM = $userORM;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        /*
        if($this->userORM->versionId >= 2)
        {
            throw new AppException(Ecode::RUNNING_ERROR);
        }else{
            $this->userORM->versionId = $this->userORM->versionId+1;
            $this->userORM->save();
        }*/
        //执行的工作任务是让他的版本号自增1
        $this->userORM->increment('versionId',1);
        //$this->userORM->save();
        //return true;
    }
}

如上我们就定义好了一个队列任务类,那么如何将这个类加入到redis中呢?又如何在后台去执行这个任务呢?

任务类的使用

<?php

namespace App\Http\Controllers;

use App\Jobs\UserVersionJob;
use App\Services\User\UserService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\RateLimiter;

class TestController extends Controller
{
    public function index5(UserService $userService)
    {
        $userORM = $userService->get(1);
        
        //分派此任务,因我设置的队列匹配redis,所以执行到这一步后,此任务会被加入到redis队列
        UserVersionJob::dispatch($userORM);
    }
}

如上所示,我们通过此方法将任务派发到了redis。有人可能会问,我传递是一个ORM对象,这样也可以吗?其实是可以的,内容中的ORM会被序列化只取其中的主键ID存入了redis,当这个任务从redis中获取出来时,会自动进行数据库操作重新生成userORM(userORM 就是Eloquent ORM 对象)

任务类的执行
在命令行中运行,即可监听任务的使用

php artisan queue:work

有时,我们还会为每个任务或是每一类任务定义一个专属名称的对垒,比如一班的所有人工作任务,二班所有人的工作任务,其实本质就是给每个任务放置到之列的任务队列上去。

<?php

namespace App\Http\Controllers;

use App\Jobs\UserVersionJob;
use App\Services\User\UserService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\RateLimiter;

class TestController extends Controller
{
    public function index5(UserService $userService)
    {
        $userORM = $userService->get(1);
        
        //my_queue_name 就是自定义的队列名称,接受字符串
        UserVersionJob::dispatch($userORM)->onQueue('my_queue_name');
    }
}

运行指定队列任务的执行

php artisan queue:work  --queue=my_queue_name

当然,队列消息还有许多其他的特性,比如unique 单一任务特性,队列运行时关于进程的睡眠(其实我很推荐这个,当没有任务需要执行的时候,可以让进程进入睡眠状态,节省服务器资源。),队列优先级,批任务的处理

队列睡眠参数–sleep,如下,含义为当没有任务的时候进程睡眠3秒后再次获取是否有任务需要执行,如有就执行任务,没得再次睡眠3秒

php artisan queue:work  --queue=my_queue_name  --sleep=3

说道这里不得不说一个安全性的问题。
特殊场景,当多个任务依赖同一个userORM时,第1个任务就修改了userORM对象的属性数据,那么之后再执行的任务是否拿到的是一个脏数据(即是没有被任务1修改后的数据,这里我做过测试以及查阅过文档:内容中的ORM会被序列化只取其中的主键ID存入了redis,当这个任务从redis中获取出来时,会自动进行数据库操作重新生成userORM)

/**
     * Notes:任务链
     * Author:tanyong
     * DateTime:2022/5/10
     */
    public function byRun()
    {
        $userORM = (new UserService())->get(1);

        $logService = new LogService();

        $batch = Bus::batch([
            new UserVersionJob($userORM),
            new UserVersionJob($userORM),
            new UserVersionJob($userORM),
            new UserVersionJob($userORM)
        ])->then(function() use($userORM,$logService){
            // 所有任务都成功完成...
            $logService->add('then handler success',0);
        })->catch(function() use($userORM,$logService){
            // 检测到第一批任务失败...
            $logService->add('exception handler success',0);
        })->finally(function() use($userORM,$logService){
            // 批处理已完成执行...
            $logService->add('finally handler success',0);
        })->dispatch();

        dd($batch->id);
    }

如上所示任务链,每个任务都会对userORM对象进行操作,但是userORM对象每次在执行时都会通过id重新去数据库获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值