php统一管理crontab

1.介绍

统一管理crontab计划任务的关键点,在于实现像crontab一样对*/10 * * * *这样的命令如何进行解析。

解析完成最后调用系统函数执行shell命令

2.实现

  • 采用语言:php
  • 借助框架:yii2

这里虽然是在yii2框架下进行实现的,但是对yii2框架的依赖极低,只是借助yii脚本执行命令而已,没有使用到其它的依赖,如换成其它框架,只需要将继承控制器修改为相应控制器实现cli的基类控制器即可。以后有时间的话会封装成一个composer组件,有问题欢迎大家留言讨论。
源码如下:

新建控制器

/console/controllers/TestCronController

添加任务

 $this->cronJobs = [
            'hello/index' => '* * * * *',
            'hello/test' => '*/10 * * * *',
        ];

添加crontab任务

* * * * * /usr/bin/php /project/yii test-cron/run
<?php
namespace console\controllers;

use yii\console\Controller;

class TestCronController extends Controller
{
    // yii脚本目录
    private $runnerScript;

    // 命令集合
    private $cronJobs = [];

    public function init()
    {
        parent::init();
        // 设置yii脚本目录,这里在最后添加了一个空格,方便后面与命令进行拼接
        $this->runnerScript = dirname(dirname(__DIR__)) . '/yii ';
        // 下面是要执行的计划任务,注意时间是不补0的数字
        $this->cronJobs = [
            'hello/index' => '* * * * *',
        ];
    }

    public function actionRun()
    {
        // 格式化当前时间戳并转成 分 时 日 月 周 格式
        /*
        |i  有前导零的分钟数 00 到 59>
        |G  小时,24 小时格式,没有前导零 0 到 23
        |j  月份中的第几天,没有前导零 1 到 31
        |n  数字表示的月份,没有前导零 1 到 12
        |w  星期中的第几天,数字表示 0(表示星期天)到 6(表示星期六)
        */
        $now = explode(' ', date('i G j n w', time()));
        $raw = $this->parseCron($this->cronJobs);
        foreach ($raw as $command => $cron) {
            // 上面已经列出了所有的情况,所以当前时间循环时如果有一项不符合则不能向下执行
            foreach ($now as $k => $piece) {
                if (!in_array($piece, $raw[$command][$k])) {
                    continue 2;
                }
            }
            // 下面是调用系统函数执行shell命令
            $this->runCommandBackground($command);
        }
    }

    /**
     * 解析需要执行的命令
     * @param $cronJobs
     * @return array
     */
    public function parseCron($cronJobs)
    {
        // 解析后的数组
        $raw = [];
        foreach ($cronJobs as $command => $cron) {
            // $command -> hello/index  $cron -> */5 * * * *
            // 将命令用空格分割成数组
            $cronArr = explode(' ', $cron, 5); // ['*/5', '*', '*', '*', '*']
            // 针对每一个位置进行解析
            $dimensions = array(
                array(0, 59), //Minutes
                array(0, 23), //Hours
                array(1, 31), //Days
                array(1, 12), //Months
                array(0, 6),  //Weekdays
            );
            foreach ($cronArr as $key => $item) {
                // 标记是哪种命令格式,通过使用的crontab命令可以分为两大类
                // 1.每几分钟或每小时这样的 */10 * * * *
                // 2.几点几分这样的 10,20,30-50 * * * *
                list($repeat, $every) = explode('/', $item, 2) + [false, 1];
                if ($repeat === '*') {
                    $raw[$command][$key] = range($dimensions[$key][0], $dimensions[$key][1]);
                } else {
                    // 处理逗号拼接的命令
                    $tmpRaw = explode(',', $item);
                    foreach ($tmpRaw as $tmp) {
                        // 处理10-20这样范围的命令
                        $tmp = explode('-', $tmp, 2);
                        if (count($tmp) == 2) {
                            $raw[$command][$key] = array_merge($raw[$command][$key], range($tmp[0], $tmp[1]));
                        } else {
                            $raw[$command][$key][] = $tmp[0];
                        }
                    }
                }
                // 判断*/10 这种类型的
                if ($every > 1) {
                    foreach ($raw[$command][$key] as $k => $v) {
                        if ($v % $every != 0) {
                            unset($raw[$command][$key][$k]);
                        }
                    }
                }
            }
        }
        return $raw;
    }

    /**
     * 以守护进程模式执行命令
     * @param $command
     */
    public function runCommandBackground($command)
    {
        system($this->runnerScript . $command . ' &');
    }
}
发布了48 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_37825371/article/details/92654472