thinkphp6+swoole实现crontab项目定时任务

背景

之前做定时任务,都是通过linux的crontab来实现,但是定时任务多了,就不好管理。一个是是定时的备注不好写,另一个是定时日志的结果没法存入数据库。

需求

1、按crontab的格式设置定时任务

2、执行结果存入数据库,包括执行结果,执行时间

3、秒级的定时

安装依赖

composer require dragonmantank/cron-expression

代码实现

1、添加一个自定义指令 Task

php think make:command Task task

2、在config/console.php中注册该指令

// 指令定义

'commands' => [

        'task' => \app\command\Task::class,

],

3、完善指令代码

setName('task')
            ->setDescription('the task command');
    }

    protected function execute(Input $input, Output $output)
    {
        $output->writeln('Execute start!'.Date('Y-m-d H:i:s'));
        run(function () use($output) {
            $time = time();

            //筛选未过期且未完成的任务
            $crontabList = Db::name('system_task')->where('status', '=', '1')->order('weigh DESC,id DESC')->select();

            // 准备需要执行的crontab
            $out_crontab_arr = [];
            $max_crontab_arr = [];
            $exe_crontab_arr = [];

            foreach ($crontabList as $key => $crontab) {
                if ($time < $crontab['begintime']) {
                    $output->writeln('任务未开始');
                    continue;
                }
                if ($crontab['maximums'] && $crontab['executes'] > $crontab['maximums']) {
                    //任务已超过最大执行次数
                    $output->writeln('任务已超过最大执行次数');
                    array_push($max_crontab_arr,$crontab);
                } else {
                    if ($crontab['endtime'] > 0 && $time > $crontab['endtime']) {
                        //任务已过期
                        $output->writeln('任务已过期');
                        array_push($out_crontab_arr,$crontab);
                    } else {
                        //重复执行
                        //如果未到执行时间则继续循环
                        $cron = CronExpression::factory($crontab['schedule']);
                        if (!$cron->isDue(date("YmdHi", $time)) || date("YmdHi", $time) === date("YmdHi", $crontab['executetime'])) {
                            $output->writeln('未到执行时间则继续循环');
                            continue;
                        }else{
                            array_push($exe_crontab_arr,$crontab);
                        }
                    }
                }
            }
            if($out_crontab_arr){
                $out_id_arr = array_column($out_crontab_arr,'id');
                Db::name('system_task')->where('id','in',$out_id_arr)->update(['status'=>3]);
            }
            if($max_crontab_arr){
                $max_id_arr = array_column($max_crontab_arr,'id');
                Db::name('system_task')->where('id','in',$max_id_arr)->update(['status'=>2]);
            }
            if($exe_crontab_arr){
                $chan = new Coroutine\Channel(count($exe_crontab_arr));
                $port = Config::get('swoole.server.port');


                foreach($exe_crontab_arr as $key => $crontab){
                    $log = [
                        'crontab_id'   => $crontab['id'],
                        'content'      => '',
                        'status'       => '', // 暂时不知道
                        'create_datetime' => Date('Y-m-d H:i:s')
                    ];
                    $crontab['log_id'] = Db::name("system_task_log")->insertGetId($log);
                    // 
                    go(function () use ($chan,$crontab,$port) {
                        $curl = new Curl();
                        $curl->setOpt(CURLOPT_TIMEOUT,50);
                        $curl->get('http://0.0.0.0:'.$port.$crontab['content']);
                        // $res = $curl->response;
                        $curl->close();
                        if(is_string($curl->response)){
                            $chan->push([$crontab['log_id'] => ['msg'=>'执行完毕','data'=>$curl->response,'update_time'=>time()]]);
                        }else{
                            $chan->push([$crontab['log_id'] => ['msg'=>'执行超时或异常','data'=>$curl->response,'update_time'=>time()]]);
                        }
                        
                    });
                }
                $result = [];
                $log_res_arr = [];
                for ($i = 0; $i < count($exe_crontab_arr); $i++)
                {
                    $result += $chan->pop();
                }
                
                foreach($result as $log_id => $res_data){
                    $update_datetime = Date('Y-m-d H:i:s',$res_data['update_time']);
                    if($res_data['msg'] == '执行完毕'){
                        $res = json_decode($res_data['data'],true);
                        if(isset($res['code'] ) && $res['code'] == 200){
                            array_push($log_res_arr,['id'=>$log_id,'status'=>'success','content'=>'成功','update_datetime'=>$update_datetime]);
                        }else{
                            $fail_msg = isset($res['msg']) ? $res['msg'] : '无msg返回,未知错误';
                            array_push($log_res_arr,['id'=>$log_id,'status'=>'failure','content'=>$fail_msg,'update_datetime'=>$update_datetime]);
                        }
                    }else{
                        array_push($log_res_arr,['id'=>$log_id,'status'=>'failure','content'=>$res_data['msg'],'update_datetime'=>$update_datetime]);
                    }
                }
                $model = new SystemTaskLog();
                $model->saveAll($log_res_arr);
            }
        });
        // 指令输出
        $output->writeln('Execute completed!'.Date('Y-m-d H:i:s'));
    }
}

在linux中设置该定时

crontab -e

* * * * * /www/server/php/73/bin/php /www/wwwroot/abc.com/think task  >> /www/server/cron/abc.com.log 2>&1

设置定时任务

查看定时结果

秒级定时

因为我们用的 cron-expression 是分钟级的,如果执行到秒级,其实是做不到的。

目前是任务里做一分钟的分割,以下做了简单粗暴的处理,当然如果项目里面有很多秒级任务,建议自己优化,或者其他方案

    public function time20s()
    {

        $this->doOnce();
        //在指定的时间后执行函数,20秒后执行
        \Swoole\Timer::after(20000, function() use ($url) {
            $this->doOnce();
            \Swoole\Timer::after(20000, function() use ($url) {
                  $this->doOnce();
            });
        });

    }

注意点

1、设置50秒超时,防止任务执行过长,所有任务的执行结果都得不到

thinkphp6+swoole实现crontab项目定时任务_第1张图片

你可能感兴趣的:(thinkphp,swoole,swoole,后端)