Laravel基于 Redis 队列处理耗时任务
hi,我是温新,一名PHPer
输出即是进步
在项目中,会遇到 “耗时任务”,而 Laravel 中的队列可以很好的对这类任务进行异步处理。如用户上传大文件,可以先对其他信息进行操作,而对大文件的操作甩给队列做,队列处理完成后再将相关信息更新回去。
本篇案例基于 Laravel9 演示,需求--用户上传图片后,对图片进行裁剪
第一步:安装一个全新的 Laravel 项目(省略)
第二步:配置 redis 作为对队列驱动
// .env
QUEUE_CONNECTION=redis
第二步:创建相关模型与控制器
// 1、创建模型-迁移文件-控制器
php artisan make:Demo -mc
// 2、编写迁移文件
Schema::create('demos', function (Blueprint $table) {
$table->id();
$table->string('title')->comment('标题');
$table->string('image')->comment('图片');
$table->timestamps();
});
// 3、执行迁移文件
php artisan migrate
第三步:安装扩展包
composer reuqire laravel/ui
php artisan ui bootstrap
npm install
npm run dev
composer require intervention/image
第四步:注册路由
// web.php
Route::get('demos/create', [\App\Http\Controllers\DemoController::class, 'create']);
Route::post('demos/store', [\App\Http\Controllers\DemoController::class, 'store']);
Route::get('demos/{id}', [\App\Http\Controllers\DemoController::class, 'show']);
第五步:编写控制方法
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class DemoController extends Controller
{
public function create()
{
return view('demos.create');
}
public function store(Request $request)
{
$demo = new Demo($request->all());
if ($demo->save()) {
$image = $request->file('image');
// 获取图片扩展名
$ext = $image->getClientOriginalExtension();
$name = mt_rand(100000, 999999) . '.' . $ext;
// 获取图片二进制数据并使用 base64 进行编码
$content = base64_encode($image->getContent());
// 将图片裁剪工作推送到 demo-uploads 队列进行异步处理【异步处理关键所在】
DemoUploadProcessor::dispatch($name, $content, $demo)->onQueue('demo-uploads');
return redirect('demos/' . $demo->id);
}
}
public function show($id)
{
$demo = Demo::find($id);
return view('demos.show', compact('demo'));
}
}
第六步:创建相关视图
// demos/create.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<div class="card">
<div class="card-header">发布新文章</div>
<div class="card-body">
<form action="/demos/store" method="POST" enctype="multipart/form-data">
@csrf
<div class="form-group">
<label for="title">文章标题:</label>
<input type="title" class="form-control" name="title" id="title">
</div>
<div class="form-group">
<label for="image">封面图片:</label>
<input type="file" class="form-control" id="image" name="image">
</div>
<button type="submit" class="btn btn-primary mt-3">发布文章</button>
</form>
</div>
</div>
</div>
@endsection
// demos/show.blade.php
@extends('layouts.app')
@section('content')
<div class="contaner">
<h3>{{$demo->title}}</h3>
<p>
@if ($demo->image)
<img src="{{ '/storage/demos/' . $demo->image }}" width="200">
@endif
</p>=
</div>
@endsection
第七步:创建图片任务处理类
1)创建任务类
php artisan make:job DemoUploadProcessor
2)编写任务类
<?php
namespace App\Jobs;
use App\Models\Demo;
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;
use Illuminate\Support\Facades\Storage;
class DemoUploadProcessor implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// 文件名
public string $name;
// demo 实例
public Demo $demo;
// 最大尝试次数,超过标记为执行失败
public int $tries = 10;
// 最大异常数,超过标记为执行失败
public int $maxExceptions = 3;
// 超时时间,3 分钟,超过则标记为执行失败
public int $timeout = 180;
public function __construct(string $name, string $content, Demo $demo)
{
$this->name = $name;
$this->content = $content;
$this->demo = $demo;
}
public function handle()
{
$path = mt_rand(10000, 99999) . $this->name;
// 判断文件是否存在
if (Storage::disk('public')->exists($path)) {
return;
}
$content = base64_decode($this->content);
// 若图片未保存成功则 5s 后重试
if (Storage::disk('public')->put('demos/' . $path, $content)) {
$this->demo->image = $path;
if ($this->demo->save()) {
$url = Storage::disk('public')->url('demos/' . $path);
// 保存成功后裁剪图片
\Intervention\Image\Facades\Image::make($url)->crop(300,300,50,50)->save(public_path(). '/crop-demos/' . $path);
}
} else {
$this->release(5);
}
}
}
第八步:执行队列
php artisan queue:work --queue=events,demo-uploads --tries=3
第九步:访问发布图片
访问 域名/demos/create
进行添加图片
请登录后再评论