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

解决的问题:

● 耗时较长

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

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

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

流程简述

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

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

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

助手类源码如下<?php

// +----------------------------------------------------------------------

// | Do what we can do

// +----------------------------------------------------------------------

// | Date : 2019/9/11 - 9:25 AM

// +----------------------------------------------------------------------

// | Author: seebyyu :)

// +----------------------------------------------------------------------

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');

}

}

调用过程如下<?php

namespace App\Jobs;

use App\Lib\Support\MissionFrom;

use Illuminate\Bus\Queueable;

use Illuminate\Queue\SerializesModels;

use Illuminate\Queue\InteractsWithQueue;

use Illuminate\Contracts\Queue\ShouldQueue;

use Illuminate\Foundation\Bus\Dispatchable;

/**

* Excel 导入

*

* Class importExcel

* @package App\Jobs

*/

class importExcel implements ShouldQueue

{

use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, MissionFrom;

/**

* 任务运行的超时时间。

*

* @var int

*/

public $timeout = 300;

/**

* @var string

*/

public $sheet;

/**

* importExcel constructor.

* @param $sheet

*/

public function __construct($sheet = '')

{

$this->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())]);

}

}

....

达到的效果

b870c1d6a31ab0408e645d7b1510f65f.png

注意事项

QUEUE_DRIVER=sync 变更为 redis

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

队列排错参考:

最后

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值