后台执行超长时间任务解决方案

发布时间 - 2019-09-23 00:00:00    点击率:

解决的问题:

● 耗时较长

● 各端无法调取相关任务进度进行反馈

● 自定义任务过后反馈结果

● 请教下,Laravel 如何让程序在后台执行超长时间的代码?

流程简述

● 使用异步队列执行相关任务

● 使用助手方法进行任务 / 进度创建

● 通过暴露接口反馈相关进度

助手类源码如下

 :)
// +----------------------------------------------------------------------
namespace App\Lib\Support;
trait MissionFrom
{
    /**
     * 标记前缀 模块名称#业务模块#板块标记
     *
     * @var string
     */
    public $prefix = 'school:task:default';
    /**
     * 任务详情
     * @var array
     */
    public $original = [];
    /**
     * Redis 链接
     *
     * The Redis factory implementation.
     *
     * @var \Illuminate\Redis\Connections\Connection
     */
    protected $redis;
    /**
     * 任务存在有效期
     *
     * @var int
     */
    protected $seconds = 600;
    /**
     * 创建任务
     *
     * @param string $sheet
     * @param int $len 总长度
     * @return string
     */
    public function createTask($sheet = '', $len = 100)
    {
        $sheet = $sheet ?: $this->sheet();
        $detail = [
            //  开始时间
            'begin' => time(),
            //  标记号
            'sheet' => $sheet,
            //  总长度
            'total_len' => $len,
            //  当前长度
            'schedule' => 0
        ];
        //  主体信息
        $this->connect()->setex($this->prefix. ':'. $sheet, $this->seconds, serialize($detail));
        //  初始化任务进度
        $this->connect()->setex($this->prefix. ':schedule:'. $sheet, $this->seconds, 1);
        return $sheet;
    }
    /**
     * 设置任务内容
     *
     * @param $sheet
     * @param $value
     * @return MissionFrom
     */
    public function setTaskContent($sheet, $value)
    {
        if( $this->connect()->exists($this->prefix. ':'. $sheet)){
            $this->connect()->setex($this->prefix. ':content:'. $sheet, $this->seconds, serialize($value));
        }
        return $this;
    }
    /**
     * 获取任务内容
     *
     * @param $sheet
     * @return MissionFrom
     */
    public function getTaskContent($sheet)
    {
        return empty($data = $this->connect()->get($this->prefix. ':content:'. $sheet)) ? null : unserialize($data);
    }
    /**
     * 设置任务前缀
     *
     * @param string $prefix
     * @return $this
     */
    public function setPrefix($prefix = '')
    {
        $this->prefix = 'school:task:'. ($prefix ?: 'default');
        return $this;
    }
    /**
     * 任务详情
     *
     * @param string $sheet
     * @return array
     */
    public function taskDetail($sheet = '')
    {
        $detail = $this->connect()->get($key = ($this->prefix. ':'. $sheet));
        if( !empty($detail)){
            $this->original = array_merge( unserialize($detail), [
                'schedule' => (int)$this->getSchedule($sheet),
                'content' => $this->getTaskContent($sheet)
            ]);
        }
        return (array) $this->original;
    }
    /**
     * 进度递增
     *
     * @param string $sheet
     * @return int
     */
    public function increments($sheet = '')
    {
        $inc = 0;
        if( !empty($detail = $this->taskDetail($sheet)) &&
            $detail['schedule'] < $detail['total_len']){
            $inc = $this->connect()->incr($this->prefix. ':schedule:'. $sheet);
        }
        return $detail['schedule'] ?? $inc;
    }
    /**
     * 获取任务进度
     *
     * @param string $sheet
     * @return string
     */
    public function getSchedule($sheet = '')
    {
        return $this->connect()->exists($key = ($this->prefix. ':schedule:'. $sheet)) ? $this->connect()->get($key) : 0;
    }
    /**
     * 生成任务单号
     */
    private static function sheet()
    {
        return md5(\Hash::make(date('YmdHis')));
    }
    /**
     * 所有任务进度
     *
     * @return array
     */
    public function taskAll()
    {
        $task_group_list = [];
        //  分组
        foreach( (array)$this->connect()->keys('school:task:*') as $task) {
            if( count($task_item = explode(':', $task)) == 4){
                list($model, $model_name, $business, $key) = $task_item;
                $task_group_list[$business][] = $this->setPrefix($business)->taskDetail($key);
            }
        }
        return $task_group_list;
    }
    /**
     * @return \Illuminate\Foundation\Application|mixed
     */
    public function connect()
    {
        return app('redis.connection');
    }
}

调用过程如下

sheet = $sheet;
    }
    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        //  自定义业务前缀
        $prefix = 'export_students';
        //  创建任务进度
        $this->sheet = $this->setPrefix($prefix)->createTask($this->sheet, 20);
        //  开始执行任务
        echo '任务开始:'. $this->sheet. "\n";
        for ($i = 1; $i <= 20; $i++){
            //  延时模拟长时间任务
            sleep(rand(1, 2));
            //  进度 +1
            echo '任务进度:'. ($this->setPrefix($prefix)->increments($this->sheet)). "\n";
        }
        //  追加结果 任何类型
        $this->setPrefix($prefix)->setTaskContent($this->sheet, [
            'url' => 'http://www.baidu.com'
        ]);
    }
}

控制器部分

....
    /**
     * 学校pc端后台任务进度列表
     *
     * @return array
     */
    public function duties()
    {
        if( empty($key = request('key'))){
            $key = md5(\Hash::make(date('YmdHis')));
            //  创建任务
            $this->dispatch(new importExcel($key));
            return $key;
        }else{
            //  查询单条任务信息
            //  $this->setPrefix('export_students')->taskDetail($key);
            return success(['data' => array_merge([
                //  导出每餐记录列表
                'meal_records' => [],
                //  每日记录列表
                'daily_records' => [],
                //  其他记录列表
                'other_records' => [],
                //  照片库
                'photo_gallery' => [],
                //  采购计划
                'purchasing_plan' => [],
                //  凭证记录
                'voucher_records' => [],
                //  食材库
                'ingredient_records' => [],
                //  导入学生
                'import_students' => [],
                //  导出学生
                'export_students' => []
            ], $this->taskAll())]);
        }
    }
    ....

达到的效果

注意事项

QUEUE_DRIVER=sync 变更为 redis

开发阶段强烈建议把 horizon 这玩意儿装上,Laravel 自带的报错异常我实在无力吐槽,不方便排错.

队列排错参考:

Laravel 队列:如何查看队列报错信息?

最后

● 代码上面的业务完全根据我自身项目编写,直接照搬 可能会引起不兼容。

● 分享 更多的是一种解决思路,希望能帮到后面的小伙伴。

● 如果对代码 有什么优化思路 或者 建议 也可以探讨下。

更多Laravel相关技术文章,请访问Laravel框架入门教程栏目进行学习!


# laravel  # 接口  # 异步  # redis  # 自定义  # 报错  # 的是  # 有什么  # 总长度  # 长时间  # 希望能  # 自带  # 较长  # 装上 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Laravel怎么实现模型属性转换Casting_Laravel自动将JSON字段转为数组【技巧】  香港服务器选型指南:免备案配置与高效建站方案解析  使用spring连接及操作mongodb3.0实例  香港服务器租用费用高吗?如何避免常见误区?  如何在 React 中条件性地遍历数组并渲染元素  Laravel如何实现事件和监听器?(Event & Listener实战)  郑州企业网站制作公司,郑州招聘网站有哪些?  香港服务器网站推广:SEO优化与外贸独立站搭建策略  电商网站制作价格怎么算,网上拍卖流程以及规则?  Windows10如何更改计算机工作组_Win10系统属性修改Workgroup  详解CentOS6.5 安装 MySQL5.1.71的方法  Laravel如何获取当前用户信息_Laravel Auth门面获取用户ID  佐糖AI抠图怎样调整抠图精度_佐糖AI精度调整与放大细化操作【攻略】  javascript中对象的定义、使用以及对象和原型链操作小结  如何撰写建站申请书?关键要点有哪些?  Laravel怎么调用外部API_Laravel Http Client客户端使用  百度输入法ai组件怎么删除 百度输入法ai组件移除工具  js代码实现下拉菜单【推荐】  javascript事件捕获机制【深入分析IE和DOM中的事件模型】  PHP 500报错的快速解决方法  深圳网站制作的公司有哪些,dido官方网站?  Laravel如何理解并使用服务容器(Service Container)_Laravel依赖注入与容器绑定说明  rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted  如何生成腾讯云建站专用兑换码?  Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践  软银砸40亿美元收购DigitalBridge 强化AI资料中心布局  详解jQuery中基本的动画方法  如何在Ubuntu系统下快速搭建WordPress个人网站?  JavaScript如何实现路由_前端路由原理是什么  百度输入法全感官ai怎么关 百度输入法全感官皮肤关闭  如何获取免费开源的自助建站系统源码?  Laravel如何实现API版本控制_Laravel版本化API设计方案  黑客如何通过漏洞一步步攻陷网站服务器?  Laravel Octane如何提升性能_使用Laravel Octane加速你的应用  手机软键盘弹出时影响布局的解决方法  如何为不同团队 ID 动态生成多个“认领值班”按钮  微信h5制作网站有哪些,免费微信H5页面制作工具?  黑客如何利用漏洞与弱口令入侵网站服务器?  Laravel观察者模式如何使用_Laravel Model Observer配置  Java垃圾回收器的方法和原理总结  javascript基于原型链的继承及call和apply函数用法分析  Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】  javascript中数组(Array)对象和字符串(String)对象的常用方法总结  Edge浏览器提示“由你的组织管理”怎么解决_去除浏览器托管提示【修复】  公司网站制作价格怎么算,公司办个官网需要多少钱?  Gemini怎么用新功能实时问答_Gemini实时问答使用【步骤】  Midjourney怎么调整光影效果_Midjourney光影调整方法【指南】  Laravel如何创建自定义Artisan命令?(代码示例)  南京网站制作费用,南京远驱官方网站?  大型企业网站制作流程,做网站需要注册公司吗?