PHP定时任务框架taskPHP3.0学习记录
- PHP定时任务框架taskPHP3.0学习记录1(TaskPHP、执行任务类的实操代码实例)
- PHP定时任务框架taskPHP3.0学习记录2(环境要求、配置Redis、crontab执行时间语法、命令操作以及Screen全屏窗口管理器)
- PHP定时任务框架taskPHP3.0的学习记录3(定时提醒功能与触发)
- PHP定时任务框架taskPHP3.0学习记录4宝塔面板bash定时任务(轮询指定json文件字段后确定是否执行、环境部署、执行日志、文件权限)
- PHP定时任务框架taskPHP3.0学习记录5环境部署常见问题及解决方案
- PHP定时任务框架taskPHP3.0学习记录6宝塔面板Web服务器Redis 扩展正常工作在命令行(CLI)无法加载的解决方案
宝塔面板手动可以执行自动无法执行问题排查及解决方案
- PHP定时任务框架taskPHP3.0学习记录
- 一、症结 问题描述
- 1.taskPHP3.0定时任务
- 2.开发需求升级
- 3.出现问题
- (1)当有新的定时任务时,先关闭进程,然后执行任务重启。
- (2)强制执行完首次任务在执行下个任务
- 二、诊断 盲目下药
- 措施1.执行查看和重启任务-无效
- 措施2.引入自动加载环境-无效
- 措施3.执行权限排查-无效
- 措施4.手动定时任务-无效
- 措施4.更换centOS-无效
- 三、溯源 代码分析
- 1.结束任务脚本执行命令
- 2.查看start.php进程
- 3.将php脚本转化为sh脚本任务是否可行
- 4.kill 直接动刀-成功
- 四、诊治 执行方案
- 1.taskkill.sh 进程监控脚本
- 2.task.sh 定时任务脚本
- 3.保留最近7天的日志
- 五、休养 开发中沉淀的sh命令
- 1.`kill` 命令-用于终止、挂起或继续进程
- 2.kill -9 $(pgrep -f start.php)-找到所有命令行中包含 start.php 的进程 ID
- 3.ps aux | grep start.php-查找正在运行的`start.php`进程
- 4.Press Ctrl-C to quit.
- 5.文件权限的 shell 命令
一、症结 问题描述
1.taskPHP3.0定时任务
执行命令操作,在终端测试,均正常。
start.php start [all|任务名] 启动 可不带参数默认all
start.php start & 挂载后台运行,liunx操作
start.php close all 结束框架 必带参数all
2.开发需求升级
在实际的生成过程中,超管是很难做到每次修改定时任务后,都通过宝塔面板后台来手动执行任务的。
需求变更:
- php新增定时任务后,生成对应的webapp.json;
- 文件设置cron_task标识:0无任务1有待执行任务;
- 设置sh脚本任务,读取webapp.json:0不执行任务,1执行任务;
3.出现问题
逻辑分析:
(1)当有新的定时任务时,先关闭进程,然后执行任务重启。
php start.php close all #结束框架
php start.php start #执行启动任务
逻辑上没问题,但是start.php close all
需要执行时间,很多时候还没有执行万就跳转到下步去执行了。
(2)强制执行完首次任务在执行下个任务
实际上php的代码运行环境是打包在sh脚本的,无法严格按照预设执行。
php start.php close all && php start.php start
于是变出现了:
- 在宝塔设置定时任务,正常;
- 正常执行一次定时任务后,不再执行后续;
- 手动执行任务,正常;
- 删除php start.php close all和php start.php start 脚本,执行正常;
- 仅保留php start.php close all,执行正常;
- 仅保留php start.php start ,无法执行。
二、诊断 盲目下药
在查看系统日志、软件日志,均无明显提示下,“有病乱投医”!
措施1.执行查看和重启任务-无效
systemctl status crond
systemctl restart crond.service
措施2.引入自动加载环境-无效
source /etc/profile
source /etc/profile 命令在 Unix 和 Linux 系统中用于重新加载或执行 /etc/profile 文件中的命令和设置。/etc/profile 是一个全局的配置文件,当用户登录到系统时,系统会读取并执行该文件中的命令和设置。但是,如果已经登录到系统并希望在不重新登录的情况下应用 /etc/profile 中的更改,可以使用 source 命令来重新加载它。
具体来说,source 命令(或它的等价命令 .,例如 . /etc/profile)在当前 shell 环境中读取并执行指定的文件。这意味着 /etc/profile 中的任何变量设置、函数定义或别名定义都将直接在当前 shell 会话中生效,而无需启动新的 shell 会话或重新登录。
措施3.执行权限排查-无效
- 文件执行权限;
- 脚本执行权限;
- 目录执行权限;
- 无效
措施4.手动定时任务-无效
宝塔面板手动执行定时任务脚本的解决方案
措施4.更换centOS-无效
无奈之举,因怀疑宝塔面板问题:
- 重新安装centOS系统;
- 重新配置php+mysql+redis运行环境;
- 重新配置各个安全组端口;
- 重新设置定时任务。
- 无效。
三、溯源 代码分析
deadline将至,九九归一回本源。
1.结束任务脚本执行命令
php start.php close all
if ($value === 'all') {
$is_daemon = self::is_daemon(array($_SERVER['PHP_SELF']));
if ($is_daemon) {
foreach (self::$_sys_pids as &$pid) {
if (DS == '\\') {
system('taskkill /f /t /im php.exe');
} else {
if (extension_loaded('posix')) posix_kill($pid, SIGTERM);
system('kill -9 ' . $pid);
}
Console::log($pid . ' close success');
}
}
}
2.查看start.php进程
ps aux | grep start.php
root 15437 0.0 0.3 213588 12272 ? S 23:00 0:00 /bin/php /www/wwwroot/path/cron/start.php worker task15什么意思
这条信息来自于 ps
命令的输出,它显示了系统上运行的进程的信息:
root
: 进程的用户是root
。在 Unix 和 Linux 系统中,root
用户是超级用户,具有对系统的完全访问权限。15437
: 这是进程的进程ID(PID),它唯一标识了系统中的这个进程。0.0
: 这是进程占用的 CPU 使用率(以百分比表示)。0.0
表示这个进程当前没有使用 CPU 时间,或者使用的 CPU 时间非常短,以至于ps
命令无法捕获到。0.3
: 这是进程占用的物理内存百分比。这表示该进程占用了系统总内存的 0.3%。213588
: 这是进程使用的虚拟内存大小(以 KB 为单位)。12272
: 这是进程使用的物理内存大小(以 KB 为单位)。?
: 这通常表示进程的终端类型,但在这里,?
表示该进程没有控制终端(即它不是一个由用户直接启动的交互式进程)。S
: 这是进程的状态。S
表示该进程处于睡眠状态(sleeping),等待某个条件(如 I/O 操作)发生。23:00
: 这是进程启动的时间。但这里的时间可能只显示了小时和分钟部分,没有日期。0:00
: 这是进程运行的总时间,以分钟和秒为单位。0:00
表示该进程还没有运行很长时间,或者它正在等待 I/O 或其他资源。/bin/php /www/wwwroot/jwcsv.kwfw.net_20080/cron/start.php worker task15
: 这是启动进程的完整命令行。它表示使用/bin/php
解释器来运行/www/wwwroot/jwcsv.kwfw.net_20080/cron/start.php
脚本,并传递了worker task15
作为参数。
3.将php脚本转化为sh脚本任务是否可行
php每次执行的脚本都会在进程中展示?能否将上面的进程,从php脚本生成文件,然后sh脚本读取后,直接运行呢?
TASKS=(
"start"
"distribute"
"worker task28"
"worker task27"
"worker task26"
# ... 其他任务 ...
)
结果:失败!
需要调整的环境太多了,还是换思路吧。
4.kill 直接动刀-成功
既然在php是优雅的执行结束进行需要时间,需要执行周期,需要环境适应。那就在sh中直接KILL,会是如何?
kill -9 $(pgrep -f start.php)-找到所有命令行中包含 start.php 的进程 ID
这个命令的作用是找到所有命令行中包含 start.php 的进程的进程 ID,然后向这些进程发送 KILL 信号(信号编号为 9),强制终止它们。
#!/bin/bash
# 查找包含 "start.php" 的进程并发送 KILL 信号
kill -9 $(pgrep -f start.php)
echo "所有包含 'start.php' 的进程已被强制终止。"
四、诊治 执行方案
1.taskkill.sh 进程监控脚本
#!/bin/bash
# 获取脚本所在的绝对路径
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# 定义工作目录为脚本所在的目录
WORKDIR="$SCRIPT_DIR"
# echo "$WORKDIR"
# JSON配置文件的路径为工作目录下的一个子目录或文件
JSON_CONFIG="$WORKDIR/webapp.json"
# 获取JSON配置文件的原始权限和所有者
ORIGINAL_OWNER=$(stat -c %U "$JSON_CONFIG")
ORIGINAL_GROUP=$(stat -c %G "$JSON_CONFIG")
ORIGINAL_PERMISSIONS=$(stat -c %a "$JSON_CONFIG")
# 检查JSON配置文件是否存在
if [ ! -f "$JSON_CONFIG" ]; then
echo "[$(date +"%Y-%m-%d %H:%M:%S")]Msg:taskPHP没有找到配置文件..."
exit 1
fi
# 读取JSON配置文件的值
cron_task=$(jq -r '.cron_task' "$JSON_CONFIG")
cron_time=$(jq -r '.cron_time' "$JSON_CONFIG")
data_time=$(jq -r '.data_time' "$JSON_CONFIG")
# 获取当前时间,并减去 13 分钟
current_time=$(date +"%Y-%m-%d %H:%M:%S")
determine_time=$(date -d "now - 13 minutes" +"%Y-%m-%d %H:%M:%S")
# 有数据更新且大于重启定时任务时间
if [[ "$cron_task" -eq 1 ]] || [[ "$determine_time" > "$data_time" ]]; then
echo "[$(date +"%Y-%m-%d %H:%M:%S")]Msg:taskPHP执行进程停止..."
# 切换到工作目录
cd "$WORKDIR" || exit 1
# 执行PHP脚本
kill -9 $(pgrep -f start.php)
echo "[$(date +"%Y-%m-%d %H:%M:%S")]Msg:taskPHP完成进程执行..."
# 获取JSON配置文件的原始权限和所有者
ORIGINAL_OWNER=$(stat -c %U "$JSON_CONFIG")
ORIGINAL_GROUP=$(stat -c %G "$JSON_CONFIG")
ORIGINAL_PERMISSIONS=$(stat -c %a "$JSON_CONFIG")
# 更新为进程标识
jq --argjson cron_task 2 --arg cron_time "$cron_time" --arg data_time "$(date +"%Y-%m-%d %H:%M:%S")" '.cron_task=$cron_task | .cron_time=$cron_time | .data_time=$data_time' "$JSON_CONFIG" > "$JSON_CONFIG.tmp" && {
mv "$JSON_CONFIG.tmp" "$JSON_CONFIG" &&
chown "$ORIGINAL_OWNER:$ORIGINAL_GROUP" "$JSON_CONFIG" &&
chmod "$ORIGINAL_PERMISSIONS" "$JSON_CONFIG"
}
else
echo "[$(date +"%Y-%m-%d %H:%M:%S")]Msg:taskPHP无进程执行任务..."
# 删除15天前的日志
# find "$WORKDIR/logs" -type f -name "*.log"
find "$WORKDIR/logs" -type f -name "*.log" -mtime +15 -exec rm {} \;
# 更新数据时间
jq --argjson cron_task "$cron_task" --arg cron_time "$cron_time" --arg data_time "$(date +"%Y-%m-%d %H:%M:%S")" '.cron_task=$cron_task | .cron_time=$cron_time | .data_time=$data_time' "$JSON_CONFIG" > "$JSON_CONFIG.tmp" && {
mv "$JSON_CONFIG.tmp" "$JSON_CONFIG" &&
chown "$ORIGINAL_OWNER:$ORIGINAL_GROUP" "$JSON_CONFIG" &&
chmod "$ORIGINAL_PERMISSIONS" "$JSON_CONFIG"
}
fi
2.task.sh 定时任务脚本
#!/bin/bash
# 获取脚本所在的绝对路径
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# 定义工作目录为脚本所在的目录
WORKDIR="$SCRIPT_DIR"
# echo "$WORKDIR"
# JSON配置文件的路径为工作目录下的一个子目录或文件
JSON_CONFIG="$WORKDIR/webapp.json"
# 获取当前时间并格式化为"YYYY-MM-DD HH:MM:SS"
CURRENT_TIME=$(date +"%Y-%m-%d %H:%M:%S")
# 获取JSON配置文件的原始权限和所有者
ORIGINAL_OWNER=$(stat -c %U "$JSON_CONFIG")
ORIGINAL_GROUP=$(stat -c %G "$JSON_CONFIG")
ORIGINAL_PERMISSIONS=$(stat -c %a "$JSON_CONFIG")
# 检查JSON配置文件是否存在
if [ ! -f "$JSON_CONFIG" ]; then
echo "[$CURRENT_TIME]Msg:taskPHP没有找到配置文件..."
exit 1
fi
# 读取JSON配置文件中的cron_task值
cron_task=$(jq -r '.cron_task' "$JSON_CONFIG")
cron_time=$(jq -r '.cron_time' "$JSON_CONFIG")
# 检查是否应该执行cron任务
if [ "$cron_task" -gt 0 ]; then
echo "[$(date +"%Y-%m-%d %H:%M:%S")]Msg:taskPHP定时任务执行中..."
# 切换到工作目录
cd "$WORKDIR" || exit 1
# 结束任务
# php start.php close all
# 执行PHP脚本
kill -9 $(pgrep -f start.php)
echo "[$(date +"%Y-%m-%d %H:%M:%S")]Msg:taskPHP定时任务进程停止..."
# 更新JSON文件
jq --argjson cron_task 0 --arg cron_time "$(date +"%Y-%m-%d %H:%M:%S")" --arg data_time "$(date +"%Y-%m-%d %H:%M:%S")" '.cron_task=$cron_task | .cron_time=$cron_time | .data_time=$data_time' "$JSON_CONFIG" > "$JSON_CONFIG.tmp" && {
mv "$JSON_CONFIG.tmp" "$JSON_CONFIG" &&
chown "$ORIGINAL_OWNER:$ORIGINAL_GROUP" "$JSON_CONFIG" &&
chmod "$ORIGINAL_PERMISSIONS" "$JSON_CONFIG"
}
# 启动任务
php start.php start
# 如果需要等待一段时间再执行exit,可以添加sleep命令
# sleep 600 # 等待10分钟
else
echo "[$CURRENT_TIME]Msg:taskPHP无执行任务..." #taskPHP无执行任务状态
# 更新数据时间
jq --argjson cron_task "$cron_task" --arg cron_time "$cron_time" --arg data_time "$(date +"%Y-%m-%d %H:%M:%S")" '.cron_task=$cron_task | .cron_time=$cron_time | .data_time=$data_time' "$JSON_CONFIG" > "$JSON_CONFIG.tmp" && {
mv "$JSON_CONFIG.tmp" "$JSON_CONFIG" &&
chown "$ORIGINAL_OWNER:$ORIGINAL_GROUP" "$JSON_CONFIG" &&
chmod "$ORIGINAL_PERMISSIONS" "$JSON_CONFIG"
}
fi
3.保留最近7天的日志
删除超过特定天数的日志文件,单独的sh脚本:使用find命令的-mtime选项。但是请注意,-mtime是基于文件的修改时间,并且它的参数是天数(四舍五入到24小时周期)。例如,要删除修改时间超过7天的所有日志文件:
find /path/to/logs -type f -name "*.log" -mtime +7 -exec rm {} \;
尝试使用 -mmin 选项(以分钟为单位)来测试时间范围,这可能会更敏感:
find "$WORKDIR/logs" -type f -name "*.log" -mmin +1440 -exec rm {} \;
五、休养 开发中沉淀的sh命令
1.kill
命令-用于终止、挂起或继续进程
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec … or kill -l [sigspec]
kill
命令在 Unix 和类 Unix 系统中用于向进程发送信号。该命令用于终止、挂起或继续进程。
基本用法:
kill [-s sigspec | -n signum | -sigspec] pid | jobspec ...
或者
kill -l [sigspec]
选项解释:
-s sigspec
或-n signum
或-sigspec
:指定要发送的信号。sigspec
是信号的名称(如TERM
),signum
是信号的编号(如15
)。不指定这些选项时,默认发送TERM
信号。pid
:进程 ID,即要发送信号的进程的唯一标识符。jobspec
:作业规范,通常在 shell 脚本或命令行界面中使用,表示一个或多个后台作业。
其他用法:
kill -l [sigspec]
:列出所有可用的信号,或者如果指定了sigspec
,则只列出匹配该规范的信号。
示例:
- 发送
TERM
信号(默认信号)给进程 ID 为 1234 的进程:
kill 1234
- 发送
KILL
信号给进程 ID 为 5678 的进程(将强制终止进程):
kill -s KILL 5678
或者
kill -9 5678
(-9
是 KILL
信号的编号)
3. 列出所有可用的信号:
kill -l
请注意,发送 KILL
信号(信号编号为 9)会强制终止进程,并且进程无法捕获或忽略该信号。因此,在使用 KILL
信号时要小心,确保想要终止该进程,并且没有其他更温和的方法可以达到目的。
2.kill -9 $(pgrep -f start.php)-找到所有命令行中包含 start.php 的进程 ID
pgrep -f 会匹配整个命令行,所以如果 start.php 是在一个更长的命令行中,它仍然会被匹配到。同时,发送 KILL 信号是一种非常强制的方法,它会立即停止进程,而不会给进程机会来执行任何清理操作。因此,在使用这个命令之前,请确保你了解它的后果,并且确实需要这样做。
如果你想要更温和地终止进程,可以先尝试发送 TERM 信号(默认的 kill 信号),如果进程没有响应,再考虑使用 KILL 信号。这可以通过在脚本中添加一个等待逻辑来实现。
#!/bin/bash
# 查找包含 "start.php" 的进程
pids=$(pgrep -f start.php)
# 检查是否找到了进程
if [ -z "$pids" ]; then
echo "没有找到包含 'start.php' 的进程。"
else
# 发送 KILL 信号给找到的进程
kill -9 $pids
echo "所有包含 'start.php' 的进程已被强制终止。"
fi
#!/bin/bash
# 假设 $pid 变量已经被设置为某个进程的 PID
pid=12345 # 这里只是一个例子,你需要替换成实际的 PID
# 发送 SIGKILL 信号给 PID 为 $pid 的进程
kill -9 $pid
# 检查进程是否已经被杀死(可选)
if ! kill -0 $pid 2>/dev/null; then
echo "进程 $pid 已经被成功杀死"
else
echo "进程 $pid 似乎没有被杀死"
fi
3.ps aux | grep start.php-查找正在运行的start.php
进程
ps aux | grep start.php
是一个在Unix和Linux系统中常用的命令组合,用于查找正在运行的start.php
进程。这个命令组合由两部分组成:ps aux
和 grep start.php
。
-
ps aux
:ps
是“process status”的缩写,用于显示当前系统中运行的进程的状态。a
选项表示显示所有用户的所有进程。u
选项表示显示用户/所有者信息。x
选项表示显示没有控制终端的进程。
因此,
ps aux
会列出系统上所有运行进程的详细信息,包括PID(进程ID)、TTY(控制终端)、时间、CPU和内存使用情况,以及命令行信息。 -
grep start.php
:grep
是一个强大的文本搜索工具,用于在文本中搜索指定的字符串或正则表达式。- 在这里,
grep start.php
会搜索包含start.php
字符串的行。
将 ps aux
和 grep start.php
通过管道 |
连接起来,意味着将 ps aux
的输出作为 grep start.php
的输入。这样,你就可以从所有进程的列表中筛选出包含start.php
的行。
这个命令组合通常用于查找特定的进程,因为它能够快速地定位到包含特定命令行参数或进程名的进程。但是需要注意的是,grep
命令本身也会作为一个进程运行,所以当你运行 ps aux | grep start.php
时,你通常会看到两行输出:一行是你要找的 start.php
进程(如果有的话),另一行是 grep start.php
命令本身。
如果你只想看到 start.php
进程而不包括 grep
命令的行,你可以使用 grep -v
选项来排除包含特定字符串的行,例如:
ps aux | grep start.php | grep -v grep
这样,grep -v grep
会从结果中排除掉包含grep
的行,只留下start.php
进程的信息(如果有的话)。
4.Press Ctrl-C to quit.
在 .sh 脚本中,Press Ctrl-C to quit. Start success 并不是一个命令。这更像是一个提示信息,告诉用户可以通过按下 Ctrl-C 来退出某个正在运行的进程或任务,并且已经成功启动。
5.文件权限的 shell 命令
修改某个目录下的 webapp.json 文件的权限为 755 并将其所有者更改为 www 用户,可以使用以下的 shell 命令:
change_permissions.sh
#!/bin/bash
# 假设 webapp.json 位于 /path/to/your/webapp.json
webapp_json_path="/path/to/your/webapp.json"
# 更改 webapp.json 的权限为 755
chmod 755 "$webapp_json_path"
# 更改 webapp.json 的所有者为 www
chown www: "$webapp_json_path"
执行 chown 命令通常需要超级用户权限(即 root 权限),所以你可能需要以 root 用户身份运行这个脚本,或者使用 sudo
sudo ./change_permissions.sh
@漏刻有时