PHP Server Monitor 3.5.2 常用 CLI 命令与 cron 托管详解
PHP Server Monitor 3.5.2 检测引擎 CLI 实测:cron/status.cron.php 全参数(-s/--status、--timeout、--uri)与防重入锁实测,容器内置循环、宿主 crontab、webcron 三种托管方式与 -s off 离线守护模式耗时实测。
PHP Server Monitor 3.5.2 常用 CLI 命令与 cron 托管详解
目标:讲清 PHP Server Monitor 3.5.2 的命令行面——核心 CLI
cron/status.cron.php的全部参数逐条实测(-s/--status、--timeout=、--uri=),以及检测任务的四种托管方式(容器内置循环、宿主 crontab、webcron、手动执行)与防重入锁机制的实测行为。全部命令与回显已在真实服务器(Ubuntu 26.04.1 LTS,Docker Compose 部署的 PSM 3.5.2,见站内《PHP Server Monitor 3.5.2 Docker Compose 部署指南》)实测通过。
一、环境信息
- PHP Server Monitor 3.5.2(官方 Dockerfile 自构建镜像,容器
phpservermon-3.5.2-web,PHP 7.4.33 CLI) - MariaDB 10.6.28,
psm_config表存放 cron 运行锁 - 容器内 Web 根目录:
/var/www/html(CLI 脚本位于/var/www/html/cron/status.cron.php) - 宿主机 crontab 可用(
/usr/bin/crontab,cron 守护进程运行中) - 实测时间:2026-11-01
二、工具概览
PSM 是传统 PHP Web 应用,没有管理类 CLI(不存在类似 php artisan server:add 的命令),命令行面聚焦在检测引擎上:
| 工具/形态 | 位置 | 用途 | 是否逐条实测 |
|---|---|---|---|
php cron/status.cron.php | 容器内 /var/www/html/cron/ | 检测引擎入口,跑一轮全部监控项 | ✅ |
-s on / -s off | 同上参数 | 常规检测 / 离线守护模式 | ✅ |
--timeout=X | 同上参数 | 单次覆盖锁超时 | ✅ |
--uri=<url> | 同上参数 | CLI 下指定站点 URL | ✅ |
| 容器内置检测循环 | 镜像 docker-entrypoint.sh | 每 PSM_REFRESH_RATE_SECONDS 秒自动跑一次 cron | ✅ |
| 宿主 crontab | 宿主机 | 按 cron 计划调用容器内 CLI | ✅ |
| webcron | HTTP 触发 cron/status.cron.php | 外部调度器(如在线 cron 服务)经 HTTP 触发 | ✅(403 拦截实测) |
maria-wait.sh | 镜像内 /usr/local/bin/ | 容器启动时等数据库就绪(内部脚本,非运维命令) | —(随容器启动验证) |
💡 说明:监控项/用户/通知渠道的管理都在 Web 界面(Servers、Users、Config 模块);批量操作可直接改库(表结构与键值见站内《PHP Server Monitor 3.5.2 配置参数详解》),数据库备份用
mariadb-dump(见部署指南 10.2)。
三、前置条件
- 容器运行中:
docker exec phpservermon-3.5.2-web php -v能输出版本(实测 PHP 7.4.33); - 已完成安装向导(
psm_config表有version键),至少 1 个active='yes'的监控项; - 用宿主 crontab 托管时,宿主机能执行
docker命令(本文以 root 操作)。
四、cron/status.cron.php 命令详解
4.1 脚本位置与运行形态
docker exec phpservermon-3.5.2-web php /var/www/html/cron/status.cron.php
每轮执行逻辑(源码 src/psm/Util/Server/UpdateManager.php 实证):查询 psm_servers 中 active='yes' 的监控项,逐个调用 StatusUpdater->update() 检测(website=curl / service=fsockopen / ping=系统 ping 命令),结果写回 psm_servers 并归档到 psm_servers_uptime/psm_servers_history,状态变化时按渠道发通知。
4.2 无参数运行(全量检测,实测)
$ docker exec phpservermon-3.5.2-web php /var/www/html/cron/status.cron.php
$ echo $?
0
实测要点:成功时完全静默(无任何输出),退出码 0。检测是否真的跑了,看数据库时间戳:
docker exec phpservermon-3.5.2-db mariadb -uphpservermonitor -pq1w2e3r4 phpservermonitor \
-e "SELECT server_id,label,status,ROUND(rtime,3) AS rtime,last_check FROM psm_servers;"
server_id label type status rtime last_check
1 SourceForge website off 0.563 2026-11-01 11:59:48
2 Gmail SMTP service off 10.016 2026-11-01 12:00:08
3 本地 MySQL 端口 service on 0.000 2026-11-01 12:00:08
4 Quzuo 官网首页 website on 0.097 2026-11-01 12:00:08
4.3 -s / --status:检测模式选择(实测)
CLI 参数解析(源码 cron/status.cron.php 的 getopt('s:', ['status:']) 实证)接受 -s 或 --status,取值映射表(源码 $possibleValues 原样):
| 传入值 | 实际模式 |
|---|---|
on / 1 / up | on——常规检测 |
off / 0 / down | off——离线守护模式(见六、) |
实测 -s on(与无参数等价,显式声明常规模式):
$ docker exec phpservermon-3.5.2-web php /var/www/html/cron/status.cron.php -s on
$ echo $?
0
-s off 的行为完全不同(只查当前 offline 的项、驻留 59 秒轮询),展开见六、。
4.4 --timeout=X:单次覆盖锁超时(实测)
默认锁超时为 PSM_CRON_TIMEOUT = 600 秒(src/includes/psmconfig.inc.php)。--timeout=x 可以在单次运行时覆盖它。实测方法:先把锁置位(模拟"上一轮还在跑"),再分别用默认超时与 --timeout=1 触发:
# 置位锁(运行时状态,等价于"有 cron 正在跑")
$ docker exec phpservermon-3.5.2-db mariadb -uphpservermonitor -pq1w2e3r4 phpservermonitor \
-e "UPDATE psm_config SET value='1' WHERE \`key\`='cron_running'; \
UPDATE psm_config SET value=UNIX_TIMESTAMP() WHERE \`key\`='cron_running_time';"
# 默认 600 秒锁:立即拒绝
$ docker exec phpservermon-3.5.2-web php /var/www/html/cron/status.cron.php
Cron is already running. Exiting.$ echo $?
0
# 等 2 秒后用 --timeout=1:锁被视为过期,放行执行
$ sleep 2
$ docker exec phpservermon-3.5.2-web php /var/www/html/cron/status.cron.php --timeout=1
$ echo $?
0
💡 说明:两个易踩的细节都在实测里——①锁冲突的输出
Cron is already running. Exiting.由die()直接打印,末尾没有换行(上面的$是下一命令提示符紧贴);②即便被锁拒绝,退出码依然是 0,监控 cron 本身的调度系统(如 cron-mail)不会因退出码发现冲突,只能靠输出文本或改用日志兜底。
4.5 --uri=:CLI 场景指定站点 URL(实测)
$ docker exec phpservermon-3.5.2-web php /var/www/html/cron/status.cron.php --uri=http://127.0.0.1:8138
$ echo $?
0
作用:CLI 没有 HTTP 上下文,--uri= 相当于临时 define('PSM_BASE_URL', ...),保证检测过程中构造的站内链接(通知邮件里的"查看详情"等)正确。参数格式为等号连接(源码按 explode('=', ltrim($argv, '--')) 解析,--uri http://... 分写两个参数不生效)。
五、防重入锁机制
5.1 锁的存储与判定
锁存在 psm_config 表(实测值):
key value
cron_running 0
cron_running_time 1793505983
cron_off_running 0
cron_off_running_time 1793505777
判定逻辑(源码原样语义):cron_running == 1 且 当前时间 - cron_running_time < 锁超时 时拒绝执行;cron 正常结束会把 cron_running 复位为 0。若上一轮崩溃未复位,锁超时(默认 600 秒,可用 --timeout 覆盖)后自动放行。
5.2 完整闭环实测
置位锁 → cron 运行 → Cron is already running. Exiting.(拒绝,见 4.4)
复位锁 value='0' → cron 运行 → 静默(EXIT=0,正常执行)
5.3 与容器内置循环碰撞的实测(宿主 crontab 触发)
容器入口脚本自带每 60 秒一轮的检测循环(见七、)。在宿主 crontab 里也加一条每分钟调用的任务,两路触发撞在同一时刻时,防重入锁让后到者退出——实测 crontab 触发后的日志文件内容(连撞两次,各自紧贴无换行):
Cron is already running. Exiting.Cron is already running. Exiting.
这正是锁机制的价值:多路触发不会导致重复检测与重复告警。实践建议:容器化部署用内置循环即可,不要再加宿主 crontab;源码部署才需要 crontab(示例见 7.2)。
5.4 -s off 有独立的锁
-s off 模式使用 cron_off_running/cron_off_running_time(conf 前缀 cron_off_,源码实证),与常规模式的 cron_running 互不干扰——两者可并行运行。
六、-s off:离线守护模式(实测)
-s off 不是"关闭检测",而是对已离线监控项的加密轮询守护:只查询 status='off' 的监控项(源码 SQL AND status = 'off' 实证),在脚本内驻留约 59 秒、每 CRON_DOWN_INTERVAL(默认 5)秒补一轮检测,让刚恢复的服务尽快被发现并告警。
实测(当时有 2 个离线项:SourceForge 403 秒拒、Gmail SMTP 10 秒超时):
$ time docker exec phpservermon-3.5.2-web php /var/www/html/cron/status.cron.php -s off
sh: 1: /usr/sbin/sendmail: not found
real 2m23.212s
user 0m0.015s
sys 0m0.008s
EXIT=0
耗时解读(与源码逻辑吻合):59 秒的"驻留窗口"按 5 秒节拍推进,但每个节拍的实际检测耗时由离线项的连接超时决定(本例每轮 ≈10.5 秒:Gmail 10s 超时 + SourceForge 0.5s),12 个节拍累计约 2 分钟以上,实测 2m23s。期间一轮检测发现某离线项状态变化,触发了邮件告警尝试(输出 sh: 1: /usr/sbin/sendmail: not found,容器无 MTA,见八、)——顺带证明 -s off 确实在持续复查离线项。监控项越多、超时越长,-s off 单轮越久,官方文档建议它与常规 cron 分开、用低频计划调用。
七、四种托管方式详解
7.1 容器内置循环(Docker 部署默认,实测)
官方镜像入口脚本 docker-entrypoint.sh 最后一行(原样):
exec "$@" & (while true; do sleep "${PSM_REFRESH_RATE_SECONDS}"; /usr/local/bin/php ${APACHE_DOCUMENT_ROOT}/cron/status.cron.php; done)
即容器启动后,后台常驻循环每 PSM_REFRESH_RATE_SECONDS 秒(本文实测设 60)执行一次检测。行为特征(实测):
- 循环无输出,
docker logs里看不到每轮检测痕迹,验证靠psm_servers.last_check时间戳推进; - 与手动执行/其他触发撞车时被锁拒绝(
Cron is already running. Exiting.,实测见 5.3); - 改间隔需
docker compose up -d重建容器(restart不重读环境变量,实测过 Recreated 流程)。
7.2 宿主 crontab(源码/二进制部署标准方式,实测)
官方文档 docs/install.rst 给出的两种典型计划(源码包内原文示例):
# 单任务:每 15 分钟一轮常规检测
*/15 * * * * root /usr/bin/php /var/www/html/phpservermon/cron/status.cron.php
# 双任务组合:常规检测 + 离线守护
*/5 * * * * root /usr/bin/php /var/www/html/phpservermon/cron/status.cron.php -s on
* * * * * root /usr/bin/php /var/www/html/phpservermon/cron/status.cron.php -s off
本文在 Docker 环境实测宿主 crontab 托管(通过 docker exec 进入容器执行):
# 安装带标记的任务(便于事后清理)
(crontab -l; echo '* * * * * docker exec phpservermon-3.5.2-web php /var/www/html/cron/status.cron.php >> /tmp/psm-crontab-test.log 2>&1 # psm-doc-test') | crontab -
# 等待下一分钟触发后查看回显
cat /tmp/psm-crontab-test.log
Cron is already running. Exiting.Cron is already running. Exiting.
(与容器内置循环同时触发,后到者被锁拒绝,证明托管链路可用且锁生效。)
实测后清理并还原 crontab:
crontab -l | grep -v 'psm-doc-test' | crontab -
⚠️ 注意:crontab 里重定向到日志文件是刻意为之——cron 默认会把命令输出发邮件给本地用户,而 PSM cron 的锁冲突文本末尾无换行、成功时无输出,落日志更易核对。
7.3 webcron:HTTP 触发(实测 403 拦截)
cron/status.cron.php 同时可经 HTTP 访问(http://<站点>/cron/status.cron.php),供外部在线 cron 服务触发。安全门槛(源码 src/bootstrap.php + 脚本头部实证):来源 IP 必须在 PSM_CRON_ALLOW 白名单(默认空数组,serialize(array())),或携带正确的 ?webcron_key=(对应 config.php 的 PSM_WEBCRON_KEY,默认空)。
本文实测:默认配置下直接 HTTP 访问被拒:
$ curl -si http://127.0.0.1:8138/cron/status.cron.php
HTTP/1.0 403 Forbidden
Date: Sun, 01 Nov 2026 04:01:38 GMT
Server: Apache/2.4.54 (Debian)
X-Powered-By: PHP/7.4.33
Content-Length: 397
Connection: close
Content-Type: text/html; charset=UTF-8
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html>
<head><title>403 Forbidden</title></head>
<body>
<h1>Forbidden</h1><p>IP address not allowed. See the
<a href="http://docs.phpservermonitor.org/en/latest/install.html#cronjob-over-web">documentation</a>
for more info.</p>
</body>
</html>
放行方式二选一:①config.php 设 define('PSM_WEBCRON_KEY', '<随机串>') 后用 .../cron/status.cron.php?webcron_key=<随机串> 访问(成功时输出 OK 后执行检测);②在 PSM_CRON_ALLOW 里写白名单 IP(默认该常量未在 config.php 定义,bootstrap.php 兜底为空数组——需自行添加 define,如 define('PSM_CRON_ALLOW', serialize(array('192.168.1.10'))))。
7.4 手动执行
即四、的所有命令:排障、验证、跑临时检测时直接 docker exec ...(或源码部署下 php cron/status.cron.php)。本文全部实测命令即手动形态。
八、注意事项
- 频率设计:常规检测周期 ≥ 最慢单项超时 × 项数/并发能力;
-s off单轮可能远超 59 秒(见六、实测 128 秒),与常规 cron 叠加时注意锁冲突属正常现象; - 退出码恒 0:被锁拒绝也是 0,靠退出码判断成败不可靠,以输出文本/落库时间戳为准;
- 通知依赖:检测触发告警邮件走
mail()或 SMTP(容器内无 MTA 时邮件失败但检测不受影响,实测日志sh: 1: /usr/sbin/sendmail: not found); - 运行用户:源码部署建议以 Web 同一用户(www-data)跑 cron,避免会话/日志文件属主混杂;Docker 形态在容器内执行天然一致;
- webcron 谨慎放行:
webcron_key一旦泄露任何人都能触发检测(打满目标站点),建议配合 HTTPS 与随机长 key。
九、常见问题(FAQ)
Q1:手动跑 cron 没有任何输出,怎么确认它真的执行了?
查 psm_servers.last_check(4.2 实测命令)或 psm_servers_uptime 行数增长;成功时静默是设计行为。
Q2:Cron is already running. Exiting. 一直出现?
说明上一轮还没结束(检测多、超时长)或频率设得过密。排查:SELECT * FROM psm_config WHERE \key` LIKE 'cron%'看cron_running_time与当前时间的差值;确属卡死可复位cron_running='0'`(600 秒后锁也会自动过期)。
Q3:--timeout=0 能永久关锁吗?
可以——源码判定 $cron_timeout > 0 才生效,设 0 等于禁用单次锁检查(PSM_CRON_TIMEOUT 常量同理)。但禁用后多路触发会真的并发重复检测,不建议。
Q4:webcron 访问 403 但我已设置 PSM_WEBCRON_KEY?
检查三点:config.php 里 define 是否被 Docker 入口脚本重写覆盖(PSM_AUTO_CONFIGURE=true 每次启动会重生成);URL 参数名必须是 webcron_key(源码原样);key 比较为字符串相等,注意前后空格。
Q5:crontab 用 root 还是 www-data 跑?
Docker 场景宿主侧随意(进容器后以容器用户执行);源码部署用与 Web 相同的用户,否则 psm_log 等写入属主不一致。官方示例(7.2)以 root 运行,配合固定绝对路径 /usr/bin/php。
十、参考链接
- 站内:《PHP Server Monitor 3.5.2 Docker Compose 部署指南》、《PHP Server Monitor 3.5.2 配置参数详解》
- 源码仓库(
cron/status.cron.php、docker-entrypoint.sh、UpdateManager.php均出自 v3.5.2 源码包):https://github.com/phpservermon/phpservermon - 官方安装文档 cron 章节(
#cronjob-over-web即 webcron 说明):https://docs.phpservermonitor.org/en/latest/install.html