126 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			126 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
namespace addons\crontab\controller;
 | 
						|
 | 
						|
use addons\crontab\model\Crontab;
 | 
						|
use Cron\CronExpression;
 | 
						|
use fast\Http;
 | 
						|
use think\Controller;
 | 
						|
use think\Db;
 | 
						|
use think\Exception;
 | 
						|
use think\Log;
 | 
						|
 | 
						|
/**
 | 
						|
 * 定时任务接口
 | 
						|
 *
 | 
						|
 * 以Crontab方式每分钟定时执行,且只可以Cli方式运行
 | 
						|
 * @internal
 | 
						|
 */
 | 
						|
class Autotask extends Controller
 | 
						|
{
 | 
						|
 | 
						|
    /**
 | 
						|
     * 初始化方法,最前且始终执行
 | 
						|
     */
 | 
						|
    public function _initialize()
 | 
						|
    {
 | 
						|
        // 只可以以cli方式执行
 | 
						|
        if (!$this->request->isCli()) {
 | 
						|
            $this->error('Autotask script only work at client!');
 | 
						|
        }
 | 
						|
 | 
						|
        parent::_initialize();
 | 
						|
 | 
						|
        // 清除错误
 | 
						|
        error_reporting(0);
 | 
						|
 | 
						|
        // 设置永不超时
 | 
						|
        set_time_limit(0);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * 执行定时任务
 | 
						|
     */
 | 
						|
    public function index()
 | 
						|
    {
 | 
						|
        $withPcntl = false;
 | 
						|
        $pool = null;
 | 
						|
 | 
						|
        $config = get_addon_config('crontab');
 | 
						|
        $mode = $config['mode'] ?? 'pcntl';
 | 
						|
        if ($mode == 'pcntl' && function_exists('pcntl_fork')) {
 | 
						|
            $withPcntl = true;
 | 
						|
            $pool = new \Jenner\SimpleFork\Pool();
 | 
						|
        }
 | 
						|
 | 
						|
        $time = time();
 | 
						|
        $logDir = LOG_PATH . 'crontab' . DS;
 | 
						|
        if (!is_dir($logDir)) {
 | 
						|
            mkdir($logDir, 0755);
 | 
						|
        }
 | 
						|
        //筛选未过期且未完成的任务
 | 
						|
        $crontabList = Crontab::where('status', '=', 'normal')->order('weigh DESC,id DESC')->select();
 | 
						|
        $execTime = time();
 | 
						|
        foreach ($crontabList as $crontab) {
 | 
						|
            $update = [];
 | 
						|
            $execute = false;
 | 
						|
            if ($time < $crontab['begintime']) {
 | 
						|
                //任务未开始
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            if ($crontab['maximums'] && $crontab['executes'] > $crontab['maximums']) {
 | 
						|
                //任务已超过最大执行次数
 | 
						|
                $update['status'] = 'completed';
 | 
						|
            } else {
 | 
						|
                if ($crontab['endtime'] > 0 && $time > $crontab['endtime']) {
 | 
						|
                    //任务已过期
 | 
						|
                    $update['status'] = 'expired';
 | 
						|
                } else {
 | 
						|
                    //重复执行
 | 
						|
                    //如果未到执行时间则继续循环
 | 
						|
                    $cron = CronExpression::factory($crontab['schedule']);
 | 
						|
                    if (!$cron->isDue() || ($execTime && date("YmdHi", $execTime) === date("YmdHi", $crontab['executetime']))) {
 | 
						|
                        continue;
 | 
						|
                    }
 | 
						|
                    $execute = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            // 如果允许执行
 | 
						|
            if ($execute) {
 | 
						|
                $update['executetime'] = $time;
 | 
						|
                $update['executes'] = $crontab['executes'] + 1;
 | 
						|
                $update['status'] = ($crontab['maximums'] > 0 && $update['executes'] >= $crontab['maximums']) ? 'completed' : 'normal';
 | 
						|
            }
 | 
						|
 | 
						|
            // 如果需要更新状态
 | 
						|
            if (!$update) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            // 更新状态
 | 
						|
            $crontab->save($update);
 | 
						|
            Db::connect()->close();
 | 
						|
            // 将执行放在后面是为了避免超时导致多次执行
 | 
						|
            if (!$execute) {
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
 | 
						|
            $runnable = new \addons\crontab\library\CommandRunnable($crontab);
 | 
						|
            if ($withPcntl) {
 | 
						|
                $process = new \Jenner\SimpleFork\Process($runnable);
 | 
						|
                $name = $crontab['title'];
 | 
						|
                $pool->execute($process);
 | 
						|
            } else {
 | 
						|
                $runnable->run();
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        if ($withPcntl && $pool) {
 | 
						|
            $pool->wait();
 | 
						|
        }
 | 
						|
        return "Execute completed!\n";
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
}
 |