php:代码中怎么搭建一个类似linux系统的crontab服务
一、前言
最近使用自己搭建的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:saturdayif ($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" == "" ]]; thenecho "请先输入【php bin路径】和【项目根目录路径】"exit
fiif [[ "$appEnv" != "pro" && "$appEnv" != "dev" ]]; thenecho "请输入环境变量参数:pro生产环境 或 dev测试环境"exit
fiif [[ ! -e "$phpBin" ]]; thenecho "【php bin路径】不正确,可尝试使用which php来获取bin路径"exit
fiif [[ ! -e "$projectPath/index.php" ]]; thenecho "【项目根目录路径】不正确"exit
ficrontabl=`crontab -l | grep "$phpBin" | grep "$projectPath" | grep "request_uri" | grep "cli_monitor"`
if [ -z "$crontabl" ]; thenecho "* * * * * $phpBin $projectPath/index.php request_uri='/cli_monitor?APP_ENV=$appEnv'" >> /var/spool/cron/rootecho -e "cli_monitor write \033[32m[SUCCESS]\033[0m"
elseecho -e "cli_monitor already exist \033[32m[SUCCESS]\033[0m"
fi相关文章:
php:代码中怎么搭建一个类似linux系统的crontab服务
一、前言 最近使用自己搭建的php框架写一些东西,需要用到异步脚本任务的执行,但是是因为自己搭建的框架没有现成的机制,所以想自己搭建一个类似linux系统的crontab服务的功能。 因为如果直接使用linux crontab的服务配置起来很麻烦࿰…...
【LeetCode: 958. 二叉树的完全性检验 + bfs + 二叉树】
🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...
MinDoc 安装与部署
下载可执行文件 mindoc mindoc_linux_amd64.zip 上传并解压压缩包 cd /opt mkdir mindoc cd mindocunzip mindoc_linux_amd64.zip 创建数据库 CREATE DATABASE mindoc_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; 配置数据库 将解压目录下 conf/app.conf.exam…...
从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础组件实现)
目录 基础组件实现 如何将图像和文字显示到OLED上 如何绘制图像 如何绘制文字 如何获取字体? 如何正确的访问字体 如何抽象字体 如何绘制字符串 绘制方案 文本绘制 更加方便的绘制 字体附录 ascii 6x8字体 ascii 8 x 16字体 基础组件实现 我们现在离手…...
windows系统如何检查是否开启了mongodb服务
windows系统如何检查是否开启了mongodb服务!我们有很多软件开发,网站开发时候需要使用到这个mongodb数据库,下面我们看看,如何在windows系统内排查,是否已经启动了本地服务。 在 Windows 系统上,您可以通过…...
VS安卓仿真器下载失败怎么办?
如果网络不稳定,则VS的安卓仿真器很容易下载失败,如下 Downloaded file <USER_HOME>\AppData\Local\Temp\xamarin-android-sdk\x86_64-35_r08.zip not found for Android SDK archive https://dl.google.com/android/repository/sys-img/google_a…...
计算机网络一点事(24)
TCP可靠传输,流量控制 可靠传输:每字节对应一个序号 累计确认:收到ack则正确接收 返回ack推迟确认(不超过0.5s) 两种ack:专门确认(只有首部无数据) 捎带确认(带数据…...
视频拼接,拼接时长版本
目录 视频较长,分辨率较大,这个效果很好,不耗用内存 ffmpeg imageio,适合视频较短 视频较长,分辨率较大,这个效果很好,不耗用内存 ffmpeg import subprocess import glob import os from nats…...
制造企业的成本核算
一、生产成本与制造费用的区别 (1)生产成本,是直接用于产品生产,构成产品实体的材料成本。 包括企业在生产经营过程中实际消耗的原材料、辅助材料、备品备件、外购半成品、燃料、动力包装物以及其它直接材料,和直接参加产品生产的工人工资,以及按生产工人的工资总额和规…...
doris:高并发导入优化(Group Commit)
在高频小批量写入场景下,传统的导入方式存在以下问题: 每个导入都会创建一个独立的事务,都需要经过 FE 解析 SQL 和生成执行计划,影响整体性能每个导入都会生成一个新的版本,导致版本数快速增长,增加了后台…...
LLMs之WebRAG:STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略
LLMs之WebRAG:STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略 目录 STORM系统简介 1、Co-STORM 2、更新新闻 STORM系统安装和使用方法 1、安装 pip安装 直接克隆GitHub仓库 2、模型和数据集 两个数据集 FreshWiki数据集 WildSeek数据集 支持…...
鸿蒙HarmonyOS实战-ArkUI动画(页面转场动画)_鸿蒙arkui tab 切换动画
PageTransitionExit({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number}) 在HarmonyOS中,PageTransitionEnter和PageTransitionExit是用于控制页面切换动画的参数。它们分别表示页面进入和退出时的动画。1. type(动画类型…...
图漾相机-ROS2-SDK-Ubuntu版本编译(新版本)
文章目录 前言1.Camport ROS2 SDK 介绍1.1 Camport ROS2 SDK源文件介绍1.2 Camport ROS2 SDK工作流程1.2.1 包含头文件1.2.2 2 初始化 ROS 2 节点1.2.3 创建节点对象1.2.4 创建发布者对象并实现发布逻辑1.2.5 启动 ROS 2 1.3 ROS2 SDK环境配置与编译1.3.1 Ubuntu 20.04 下ROS2 …...
小程序的协同工作与发布
1.小程序API的三大分类 2.小程序管理的概念,以及成员管理两个方面 3.开发者权限说明以及如何维护项目成员 4.小程序版本...
解锁维特比算法:探寻复杂系统的最优解密码
引言 在复杂的技术世界中,维特比算法以其独特的魅力和广泛的应用,成为通信、自然语言处理、生物信息学等领域的关键技术。今天,让我们一同深入探索维特比算法的奥秘。 一、维特比算法的诞生背景 维特比算法由安德鲁・维特比在 1967 年提出…...
计算机网络一点事(20)
IEEE802.11 无线局域网 分类有无基础设施 星型拓扑,基本服务集BSS一基站多移动站,服务集标识符SSID不超过32b,可接入802.3 漫游:移动站从一个基本服务集切换到另一个(类似换联WiFi) 802.11帧࿱…...
java求职学习day23
MySQL 单表 & 约束 & 事务 1. DQL操作单表 1.1 创建数据库,复制表 1) 创建一个新的数据库 db2 CREATE DATABASE db2 CHARACTER SET utf8; 2) 将 db1 数据库中的 emp 表 复制到当前 db2 数据库 1.2 排序 通过 ORDER BY 子句 , 可以将查询出的结果进行排序 ( 排序只…...
Vue-cli 脚手架搭建
安装node.js 官网下载node.js安装包,地址:Node.js — Download Node.js 先在node.js即将要安装的路径下创建两个文件夹:node_cache(缓存)、node_global(全局) 点击安装包…...
认识小程序的基本组成结构
1.基本组成结构 2.页面的组成部分 3.json配置文件 4.app.json文件(全局配置文件) 5.project.config.json文件 6.sitemap.json文件 7.页面的.json配置文件 通过window节点可以控制小程序的外观...
Spring Boot 热部署实现指南
在开发 Spring Bot 项目时,热部署功能能够显著提升开发效率,让开发者无需频繁重启服务器就能看到代码修改后的效果。下面为大家详细介绍一种实现 Spring Boot 热部署的方法,同时也欢迎大家补充其他实现形式。 步骤一、开启 IDEA 自动编译功能…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...
