Skip to content

异步任务

概述

在 Web 应用开发中,有些操作可能需要较长的处理时间,如果在 HTTP 请求中直接处理这些操作,会导致用户等待时间过长,影响用户体验。异步任务就是为了解决这类问题而设计的,它允许将耗时操作放到后台执行,用户无需等待即可得到响应。

什么是异步任务?

异步任务是指将耗时的操作从主请求流程中分离出来,放到后台独立执行的一种机制。这样可以:

  • 提高系统响应速度,避免用户长时间等待
  • 合理分配系统资源,避免资源占用过高
  • 提供任务执行状态的可视化监控

适用场景

异步任务适用于以下场景:

  • 数据导入导出:处理大量数据的导入导出操作
  • 发送邮件通知:批量发送邮件或消息通知
  • 数据统计分析:需要长时间运行的数据统计和分析
  • 文件处理:大文件的上传、处理、转换等操作
  • 第三方 API 调用:调用响应较慢的外部服务

CatchAdmin 后台提供了一个完整的异步任务解决方案,包括可视化界面和易于接入的 API,使开发者能够轻松实现自己的后台异步任务。如下所示:

catchadmin 后台异步任务 Laravel Admin

目前后台导入导出功能已经实现了异步任务处理,那么该如何自定义实现自己的异步任务呢?本文将详细介绍。

WARNING

异步任务执行默认是关闭的。请先到根目录下的 routes/console.php 按照提示打开即可

异步任务表结构

php
 Schema::create('async_task', function (Blueprint $table) {
    $table->id();
    $table->string('task')->comment('task任务对应的 class 名称');
    $table->string('params')->default('')->comment('任务所需参数');
    $table->integer('start_at')->default(0)->comment('开始时间');
    $table->tinyInteger('status')->default(1)->comment('状态:un_start=1,running=2,finished=3,error=4');
    $table->integer('time_taken')->default(0)->comment('运行耗时');
    $table->string('error')->default('')->comment('执行结果错误');
    $table->string('result')->default('')->comment('执行结果');
    $table->string('retry')->default(0)->comment('重试次数');
    $table->createdAt();
    $table->updatedAt();
    $table->deletedAt();
});

定义

需要实现自定义的后台异步任务,需要实现一个异步任务接口 AsyncTaskInterface

php
namespace Catch\Contracts;

interface AsyncTaskInterface
{
    /**
     * push task
     */
    public function push(): mixed;

    /**
     * run task
     */
    public function run(array $params): mixed;
}

接口实现

php
use Modules\System\Models\AsyncTask;

class AsyncTask implements AsyncTaskInterface
{
    // push 方法将当前任务推送到任务队列
    public function push(): mixed
    {
        return app(AsyncTask::class)
            ->storeBy([
                'task' => get_called_class(),
                'params' => '', // 参数需要自定义实现
            ]);
    }


    // run 方法 任务具体业务线,它接受传进来的参数
    public function run(array $params): mixed
    {
        // params 就是 push 进去的 params 参数

        // 自定义实现任务
    }
}

使用

在需要使用 AsyncTask 的地方,将任务推送到任务列表中

php
$async = new AsyncTask()

$async->push()

添加定时任务

系统管理->定时任务 页面,添加如下图的定时任务配置 catchadmin 异步任务

启动

WARNING

下面的命令是本地测试使用,切勿在线上使用

shell
php artisan schedule:work

示例

在用户模块modules/User 目录下,提供了一个完整的示例,基于用户导出。

WARNING

导出是经过包装的,不要直接使用,你需要自己实现异步接口的 push 和 run 方法来接入实际业务

php
namespace Modules\User\Export;

use Catch\Contracts\AsyncTaskInterface;
use Catch\Support\Excel\Export;
use Modules\System\Support\Traits\AsyncTaskDispatch;

class User extends Export implements AsyncTaskInterface
{
    // 推送任务 trait
    use AsyncTaskDispatch;

    protected array $header = [
        'id', '昵称', '邮箱', '创建时间',
    ];

    // 查询出来导出的数据
    public function array(): array
    {
        // TODO: Implement array() method.
        return \Modules\User\Models\User::query()
            ->select('id', 'username', 'email', 'created_at')
            ->without('roles')
            ->get([
                'id', 'username', 'email', 'created_at',
            ])->toArray();
    }
}
php
// 推送 trait
namespace Modules\System\Support\Traits;

use Modules\System\Models\AsyncTask;

/**
 * @method array getParams()
 */
trait AsyncTaskDispatch
{
    public function push(): mixed
    {
        // 直接推送异步任务表
        return app(AsyncTask::class)
            ->storeBy([
                'task' => get_called_class(),
                'params' => count($this->getParams()) ? json_encode($this->getParams()) : '',
            ]);
    }
}

使用

php
public function export(\Modules\User\Export\User $export): mixed
{
    return $export->push();
}

用户管理 页面点击导出之后,会生成这样一条任务。如下图 catchadmin 异步任务

执行成功之后会生成如下图的 excel 文件 catchadmin 异步任务