php:代码中怎么搭建一个类似linux系统的crontab服务

news2025/1/26 14:32:50

一、前言

        最近使用自己搭建的php框架写一些东西,需要用到异步脚本任务的执行,但是是因为自己搭建的框架没有现成的机制,所以想自己搭建一个类似linux系统的crontab服务的功能。

        因为如果直接使用linux crontab的服务配置起来很麻烦,如果不了解的人接手,也不知道你配置了crontab,后续拆分生产和测试环境也会很复杂,不能一套代码包含所有。

二、配置文件

        先在相关配置目录下放一个配置文件,例如:config/crontab.ini,里面配置如下结构,其中需要注意的是request_uri参数,这个参数是各自框架中使用命令行形式执行任务的命令,可以根据自己的框架进行修改。

例:我目前的框架执行命令形式是:

php index.php request_uri="/cli_Test/test"

;【使用说明】
;1、检测机制:每分钟自动检测一次该配置
;
;2、参数说明
;request_uri:为执行任务的命令行
;time:控制启动时间,分别为分、时、日、月、周
;proc_total:运行脚本的进程数
;ip_limit:服务器ip限制(多个ip英文,分隔)


[test]
request_uri = "/cli_Test/test"
time = "0 3 * * *"
proc_total = "1"
;ip_limit = 10.235.62.241

注:

        time:配置方式是一比一复制的linux crontab配置

        proc_total:支持多个进程执行任务的方式,比如队列消费要启用多个进程消费就很方便,并且会自动检测执行任务的进程是否存在,存在不会重复启动。

        ip_limit:有时候集群服务器太多,但是你只想单台机器执行,可以使用该配置,限制执行的服务器ip是什么,也可以配置多个。

三、监控相关类

  1、配置读取类,可以解析上述配置文件结构

<?php
/**
 * 配置信息读取类
 *
 * @package Comm
 */
class Config {

	private static $_config = array();

    /**
     * 读取配置信息
     *
     * @param  string $path   节点路径,第一个是文件名,使用点号分隔。如:"app","app.product.test"
     * @return array|string
     */
    public static function get($path) {
		if(!isset(self::$_config[$path])) {
			$arr = explode('.', $path);
			try {
                $conf = parse_ini_file(APP_PATH . 'config/'.$arr[0].'.ini', true);
			} catch (Exception $e) {
			}

            if (!empty($conf)) {
                if (isset($arr[1]) && !isset($conf[$arr[1]])) {
                    throw new Exception("读取的配置信息不存在,path: " . $path);                
                }
                if (isset($arr[1])) $conf = $conf[$arr[1]];

                if (isset($arr[2]) && !isset($conf[$arr[2]])) {
                    throw new Exception("读取的配置信息不存在,path: " . $path);                
                }
                if (isset($arr[2])) $conf = $conf[$arr[2]];
            }
            if (!isset($conf) || is_null($conf)) {
                throw new Exception("读取的配置信息不存在,path: " . $path);                
			}
			self::$_config[$path] = $conf;
		}
		return self::$_config[$path];
    }
}

2、任务监控类

注:其中需要注意的是shell方法的内容,如果自己的框架不适用这种执行命令方式,可以更改为自己框架的命令。

<?php
namespace app\controllers\Cli;

/**
 * Crontab监控
 * 
 * 注:切勿轻易修改
 */
class MonitorController
{
    /**
     * 检查计划任务
     */
    public function index() 
    {
        $appEnv = \Context::param("APP_ENV");//获取运行环境
        $appEnvParam = !empty($appEnv) ? "&APP_ENV=".$appEnv : "";
        
        echo "\033[35mCheck Crontab:\r\n\033[0m";
        $config = $this->getConfig();
        foreach ($config as $key => $value) {
            if (!$this->checkTime(time(), $value['time'])) {
                echo "{$key}:[IGNORE]\r\n";
                continue;
            }
			$ip_limit = isset($value['ip_limit']) ? explode(',',$value['ip_limit']) : false;
            for ($i = 1; $i <= $value['proc_total']; ++$i) {
                $request_uri = "{$value['request_uri']}?proc_total={$value['proc_total']}&proc_num={$i}{$appEnvParam}";

                //检查进程是否存在
                $shell = $this->shell($request_uri);
                $num   = $this->shell_proc_num($shell);

                echo "{$key}_{$i}:";
                if ($num >= 1) { //进程已存在
					echo "\033[33m[RUNING]\033[0m";
                } else {  //进程不存在,操作
					if($ip_limit){
						if(in_array(\Util::getServerIp(),$ip_limit)){
							echo "\033[32m[OK]\033[0m";
							$this->shell_cmd($request_uri);
						}else{
							echo "\033[32m[IP LIMIT]\033[0m";
						}
					}else{
						echo "\033[32m[OK]\033[0m";
						$this->shell_cmd($request_uri);
					}
                }
                echo "\r\n";
            }
        }
    }

    /**
     * 获取crontab配置
     * 
     * @return	array
     */
    public function getConfig() 
    {
        return \Config::get('crontab');
    }

    /**
     * 检查是否该执行crontab了
     * 
     * @param	int		$curr_datetime	当前时间
     * @param	string	$timeStr		时间配置
     * @return	boolean
     */
    protected function checkTime($curr_datetime, $timeStr) 
    {
        $time = explode(' ', $timeStr);
        if (count($time) != 5) {
            return false;
        }

        $month  = date("n", $curr_datetime); // 没有前导0
        $day    = date("j", $curr_datetime); // 没有前导0
        $hour   = date("G", $curr_datetime);
        $minute = (int)date("i", $curr_datetime);
        $week   = date("w", $curr_datetime); // w 0~6, 0:sunday  6:saturday
        if ($this->isAllow($week, $time[4], 7, 0) &&
            $this->isAllow($month, $time[3], 12) &&
            $this->isAllow($day, $time[2], 31, 1) &&
            $this->isAllow($hour, $time[1], 24) &&
            $this->isAllow($minute, $time[0], 60)
        ) {
            return true;
        }
        return false;
    }

    /**
     * 检查是否允许执行
     * 
     * @param	mixed	$needle			数值
     * @param	mixed	$str			要检查的数据
     * @param	int		$TotalCounts	单位内最大数
     * @param	int		$start			单位开始值(默认为0)
     * @return  type
     */
    protected function isAllow($needle, $str, $TotalCounts, $start = 0) 
    {
        if (strpos($str, ',') !== false) {
            $weekArray = explode(',', $str);
            if (in_array($needle, $weekArray))
                return true;
            return false;
        }
        $array     = explode('/', $str);
        $end       = $start + $TotalCounts - 1;
        if (isset($array[1])) {
            if ($array[1] > $TotalCounts)
                return false;
            $tmps = explode('-', $array[0]);
            if (isset($tmps[1])) {
                if ($tmps[0] < 0 || $end < $tmps[1])
                    return false;
                $start = $tmps[0];
                $end   = $tmps[1];
            } else {
                if ($tmps[0] != '*')
                    return false;
            }
            if (0 == (($needle - $start) % $array[1]))
                return true;
            return false;
        }
        $tmps = explode('-', $array[0]);
        if (isset($tmps[1])) {
            if ($tmps[0] < 0 || $end < $tmps[1])
                return false;
            if ($needle >= $tmps[0] && $needle <= $tmps[1])
                return true;
            return false;
        } else {
            if ($tmps[0] == '*' || $tmps[0] == $needle)
                return true;
            return false;
        }
    }

    /**
     * 执行Shell命令
     *
     * @param   string  $request_uri
     */
    public function shell_cmd($request_uri) 
    {
        if (IS_WIN) {
            $cmd = $this->shell($request_uri);
            pclose(popen("start /B " . $cmd, "r"));
        }else{
            $cmd = $this->shell($request_uri) . " > /dev/null &";
            $pp  = @popen($cmd, 'r');
            @pclose($pp);
        }
    }

    /**
     * 获取Shell执行命令
     *
     * @param   string  $request_uri
     * @return  string
     */
    public function shell($request_uri) 
    {
        return PHP_BIN . ' ' . rtrim(APP_PATH, '/') . "/index.php request_uri=\"{$request_uri}\"";
    }

    /**
     * 检查指定shell命令进程数
     *
     * @param   string  $shell  shell命令
     * @return  int
     */
    public function shell_proc_num($shell) 
    {
        if (IS_WIN) {// Windows 环境下的逻辑
            if (!extension_loaded('com_dotnet')) {
                die("COM extension is not installed or loaded.");
            }
            $num = 0;
            $shell = str_replace([' ', '\\'], ['', '/'], $shell);
            $computer = ".";
            $obj = new \COM('winmgmts:{impersonationLevel=impersonate}!\\\\' . $computer . '\\root\\cimv2');
            $processes = $obj->ExecQuery("SELECT * FROM Win32_Process");
            foreach ($processes as $process) {
                $line = str_replace([' ', '\\'], ['', '/'], $process->CommandLine);
                if (strpos($line, $shell) !== false) {
                    $num++;
                }
            }
            return $num;
        } else {
            $shell = str_replace(array('-', '"'), array('\-', ''), $shell);
            // $shell = preg_quote($shell);
            $shell = str_replace("\?", '?', $shell);
            $cmd = "ps -ef | grep -v 'grep' |grep \"{$shell}\"| wc -l";
            $pp  = @popen($cmd, 'r');
            $num = trim(@fread($pp, 512)) + 0;
            @pclose($pp);
            return $num;
        }
    }
}

四、执行初始化脚本

1、init.sh,这个脚本主要是把监控任务类的执行方式写入到了linux中的crontab服务,后续就不用管了。

2、后续想要增加执行的任务,可以直接在crontab.ini中增加即可。

#开启crond
/sbin/service crond start

#追加写入crontab监控
phpBin=$1
projectPath=$2
appEnv=$3
if [[ "$phpBin" == "" || "$projectPath" == "" ]]; then
	echo "请先输入【php bin路径】和【项目根目录路径】"
	exit
fi

if [[ "$appEnv" != "pro" && "$appEnv" != "dev" ]]; then
	echo "请输入环境变量参数:pro生产环境 或 dev测试环境"
	exit
fi

if [[ ! -e "$phpBin" ]]; then
	echo "【php bin路径】不正确,可尝试使用which php来获取bin路径"
	exit
fi

if [[ ! -e "$projectPath/index.php" ]]; then
	echo "【项目根目录路径】不正确"
	exit
fi

crontabl=`crontab -l | grep "$phpBin" | grep "$projectPath" | grep  "request_uri" | grep "cli_monitor"`
if [ -z "$crontabl" ]; then
	echo "* * * * * $phpBin $projectPath/index.php request_uri='/cli_monitor?APP_ENV=$appEnv'" >> /var/spool/cron/root
	echo -e "cli_monitor write \033[32m[SUCCESS]\033[0m"
else
	echo -e "cli_monitor already exist \033[32m[SUCCESS]\033[0m"
fi

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2281851.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web端ActiveMq测试工具

如何用vue3创建简单的web端ActiveMq测试工具&#xff1f; 1、复用vue3模板框架 创建main.js,引入APP文件&#xff0c;createApp创建文件&#xff0c;并加载element插件&#xff0c;然后挂载dom节点 2、配置vue.config.js脚本配置 mport { defineConfig } from "vite&qu…

2K高刷电竞显示器推荐

2K高刷电竞显示器推荐&#xff0c;各位喜欢打游戏&#xff0c;身为电竞迷的小伙伴&#xff0c;如果你想选一款2K高刷电竞显示器&#xff0c;那么下面的内容不容错过。 1.HKC G27H4Pro - 2K高刷电竞显示器推荐 外观 - HKC G27H4Pro 2K高刷电竞显示器 初见 HKC G27H4Pro&#x…

他把智能科技引入现代农业领域

江苏田倍丰农业科技有限公司&#xff08;以下简称“田倍丰”&#xff09;是一家专注于粮油种植的农业科技公司&#xff0c;为拥有300亩以上田地的大户提供全面的解决方案。田倍丰通过与当地政府合作&#xff0c;将土地承包给大户&#xff0c;并提供农资和技术&#xff0c;实现利…

第38周:猫狗识别 (Tensorflow实战第八周)

目录 前言 一、前期工作 1.1 设置GPU 1.2 导入数据 输出 二、数据预处理 2.1 加载数据 2.2 再次检查数据 2.3 配置数据集 2.4 可视化数据 三、构建VGG-16网络 3.1 VGG-16网络介绍 3.2 搭建VGG-16模型 四、编译 五、训练模型 六、模型评估 七、预测 总结 前言…

OpenCV2D 特征框架 (6)特征检测与描述类cv::KAZE的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::KAZE 类是 OpenCV 库中用于实现 KAZE 特征检测和描述的类。KAZE 是一种尺度不变特征变换&#xff08;Scale-Invariant Feature Transform, S…

Unity git版本管理

创建仓库的时候添加了Unity的.gitignore模版&#xff0c;在这个时候就能自动过滤不需要的文件 打开git bash之后&#xff0c;步骤git版本管理-CSDN博客 如果报错&#xff0c;尝试重新进git 第一次传会耗时较长&#xff0c;之后的更新就很快了

Neural networks 神经网络

发展时间线 基础概念 多层神经网络结构 神经网络中一个网络层的数学表达 TensorFlow实践 创建网络层 神经网络的创建、训练与推理 推理 推理可以理解为执行一次前向传播 前向传播 前向传播直观数学表达 前向传播直观数学表达的Python实现 前向传播向量化实现 相关数学知识…

2023年吉林省职业院校技能大赛网络系统管理样题

目录 任务清单 &#xff08;一&#xff09;基础配置 &#xff08;二&#xff09;有线网络配置 &#xff08;三&#xff09;无线网络配置 &#xff08;四&#xff09;出口网络配置 附录1&#xff1a;拓扑图​编辑 附录2&#xff1a;地址规划表 任务清单 &#xff08;一&a…

C++入门14——set与map的使用

在本专栏的往期文章中&#xff0c;我们已经学习了STL的部分容器&#xff0c;如vector、list、stack、queue等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层是线性序列的数据结构&#xff0c;里面存储的是元素本身。而本篇文章我们要来认识一下关联式容器。 &am…

996引擎 - 前期准备-配置开发环境

996引擎 - 前期准备 官网搭建服务端、客户端单机搭建 开发环境配置后端开发环境配置环境 前端开发环境配置环境 后端简介前端简介GUILayoutGUIExport 官网 996传奇引擎官网 所有资料从官网首页开始&#xff0c;多探索。 文档&#xff1a; 996M2-服务端Lua 996M2-客户端Lua 搭…

Java程序员如何设计一个高并发系统?

前言 无论是职场新人还是有一定工作经验的老手&#xff0c;系统设计问题都如同悬在头顶的达摩克利斯之剑。对于新人而言&#xff0c;面试时遭遇“如何从零开始设计一个完整系统”的问题&#xff0c;往往让人瞬间大脑一片空白。系统设计的范畴广泛&#xff0c;网络资源难以全面…

RV1126画面质量三:QP调节

一&#xff0e;什么是 QP 调节&#xff1f; QP 参数调节&#xff0c;指的是量化参数调节。它主要是来调节图像的细节&#xff0c;最终达到调节画面质量的作用。QP 值和比特率成反比&#xff0c;QP值越小画面质量越高&#xff1b;反之 QP 值越大&#xff0c;画面质量越低…

渐变颜色怎么调?

渐变颜色的调整是设计中非常重要的一部分&#xff0c;尤其是在创建具有视觉吸引力和深度感的设计作品时。以下是一些在不同设计软件中调整渐变颜色的详细步骤和技巧&#xff1a; 一、Adobe Photoshop 1. 创建渐变 打开渐变工具&#xff1a; 选择工具栏中的“渐变工具”&#x…

Arduino基础入门学习——OLED显示屏的基本使用

Arduino基础入门学习——OLED显示屏的基本使用 一、前言二、准备工作三、基本使用1. OLED显示基本字符 &#xff08;数字英文基本标点符号&#xff09;2. OLED显示汉字3. 显示图片 四、 结束语 一、前言 在我们的日常开发中&#xff0c;一般有这么几种方式对数据进行展示&#…

jQuery阶段总结(二维表+思维导图)

引言 经过23天的学习&#xff0c;期间有期末考试&#xff0c;有放假等插曲。本来应该在学校里学习&#xff0c;但是特殊原因&#xff0c;让回家了。但是在家学习的过程&#xff0c;虽然在学&#xff0c;很让我感觉到不一样。但是效果始终还是差点的&#xff0c;本来17、18号左右…

无公网IP 外网访问媒体服务器 Emby

Emby 是一款多媒体服务器软件&#xff0c;用户可以在 Emby 创建自己的个人多媒体娱乐中心&#xff0c;并且可以跨多个设备访问自己的媒体库。它允许用户管理传输自己的媒体内容&#xff0c;比如电影、电视节目、音乐和照片等。 本文将详细的介绍如何利用 Docker 在本地部署 Emb…

PAT甲级-1022 Digital Libiary

题目 题目大意 一个图书有图书id&#xff0c;书名&#xff0c;作者&#xff0c;关键字&#xff0c;出版商&#xff0c;出版时间6个信息。现要查询图书的ID&#xff0c;1对应通过书名查询&#xff0c;2对应作者&#xff0c;3对应关键字&#xff08;不需要完全一致&#xff0c;包…

OpenCV:在图像中添加高斯噪声、胡椒噪声

目录 在图像中添加高斯噪声 高斯噪声的特性 添加高斯噪声的实现 给图像添加胡椒噪声 实现胡椒噪声的步骤 相关阅读 OpenCV&#xff1a;图像处理中的低通滤波-CSDN博客 OpenCV&#xff1a;高通滤波之索贝尔、沙尔和拉普拉斯-CSDN博客 OpenCV&#xff1a;图像滤波、卷积与…

二叉树的存储(下)c++

链式存储 我们可以创建两个数组L[N]、r[N]&#xff0c;分别存储i 号结点的左右孩子的编号&#xff0c;这样就可以通过数组下标实现链式访问。 本质上还是孩子表示法&#xff0c;存储的是左右孩子的信息 #include <iostream>using namespace std;const int N 1e6 10; …

回归预测 | MATLAB基于TCN-BiGRU时间卷积神经网络结合双向门控循环单元多输入单输出回归预测

效果一览 基本介绍 回归预测 | MATLAB基于TCN-BiGRU时间卷积神经网络结合双向门控循环单元多输入单输出回归预测 一、引言 1.1、研究背景及意义 在当今数据驱动的时代&#xff0c;时间序列预测已成为金融、气象、工业控制等多个领域的关键技术。随着人工智能和机器学习技术的…