1
外部控制
zhbaor edited this page 2025-07-18 15:38:42 +08:00
mower-ng 控制核心机制文档
本文档聚焦 lockfile.json 和 Web API 的核心控制机制。
1. lockfile 工作机制
1.1 文件结构
lockfile.json 是一个简单的 JSON 文件,包含三个关键信息:
{
"pid": 12345, // 当前运行的 mower-ng 进程 ID
"port": 5000, // HTTP 服务使用的端口号
"token": "abc123" // API 访问认证令牌
}
1.2 生命周期
lockfile 在 mower-ng 进程启动时自动创建,记录当前进程的关键信息。当进程正常退出时,lockfile 会被自动删除。这个文件只在进程运行时存在。
1.3 文件位置
lockfile 始终存放在当前运行的 mower-ng 实例配置目录下,文件名固定为 lockfile.json
。例如当配置目录为 ~/mower-profiles/default
时,lockfile 的完整路径就是 ~/mower-profiles/default/lockfile.json
。
1.4 异常情况处理
lockfile 的存在并不总是意味着 mower-ng 进程正在正常运行。当进程因意外情况(如系统崩溃、强制终止或程序错误)而退出时,lockfile 可能会残留在系统中。这种情况下,我们需要通过以下验证流程来判断进程的真实状态:
首先,检查 lockfile 中记录的 PID 对应的系统进程是否仍然存在。如果进程不存在,说明 mower-ng 已经异常退出,lockfile 是残留文件。
如果进程存在,我们需要进一步验证它是否是真正的 mower-ng 实例:
- 检查进程的执行文件路径是否匹配 mower-ng 的主程序
- 确认进程的命令行参数中包含的配置目录路径与当前 lockfile 所在目录一致
当出现以下情况时,可以安全删除残留的 lockfile:
- 系统进程不存在(mower-ng 已退出)
- 进程存在但不是 mower-ng 实例(PID 被其他进程占用)
- 配置目录路径不匹配当前实例(可能是其他实例的残留)
这种验证机制有效处理了多种异常场景:
- 系统意外断电导致进程退出但 lockfile 残留
- 用户手动强制终止进程后未清理文件
- 不同 mower-ng 实例的 PID 偶然重复造成混淆
以下是实现该验证逻辑的 Python 代码示例(参考 webui.py 中的实现):
import json
import os
import psutil
def validate_lockfile(lockfile_path, current_profile_path):
"""验证 lockfile 的有效性"""
if not os.path.exists(lockfile_path):
return False, "lockfile 不存在"
try:
# 读取 lockfile 内容
with open(lockfile_path, "r", encoding="utf-8") as f:
lock_data = json.load(f)
pid = lock_data["pid"]
# 检查进程是否存在
if not psutil.pid_exists(pid):
return False, f"进程 {pid} 不存在"
# 获取进程详细信息
process = psutil.Process(pid)
cmdline = process.cmdline()
# 验证执行文件路径
current_file_path = os.path.abspath(__file__)
running_file_path = os.path.abspath(cmdline[1])
if current_file_path != running_file_path:
return False, "执行文件路径不匹配"
# 验证配置目录路径
if len(cmdline) >= 3: # 确保有配置目录参数
running_profile_path = os.path.abspath(cmdline[2])
if current_profile_path != running_profile_path:
return False, "配置目录路径不匹配"
return True, "验证通过"
except Exception as e:
return False, f"验证过程中出错: {str(e)}"
# 使用示例
lockfile_path = "/path/to/profile/lockfile.json"
current_profile = "/path/to/profile"
is_valid, message = validate_lockfile(lockfile_path, current_profile)
if not is_valid:
print(f"lockfile 无效: {message}")
# 安全删除残留的 lockfile
os.remove(lockfile_path)
2. Web API 控制接口
2.1 认证机制
所有请求需携带 header:
headers = {"token": lockfile["token"]}
认证规则说明:
- 需要认证的接口:所有控制类接口(进程控制、调度器控制、配置修改等)
- 无需认证的接口:状态查询、日志流等只读接口
- 认证失败:返回 HTTP 403 状态码
2.2 核心端点说明
2.2.1 进程控制
GET /exit
- 功能:安全停止进程
- 状态转换:STOPPING → STOPPED
- 响应格式:
text/plain
("true"表示成功) - 认证要求:需要 token
2.2.2 调度器控制
GET /start
- 功能:启动任务调度
- 状态转换:STOPPED → RUNNING
- 认证要求:需要 token
GET /stop
- 功能:停止任务调度
- 状态转换:RUNNING → STOPPING → STOPPED
- 认证要求:需要 token
2.2.3 状态查询
GET /scheduler
- 认证要求:无需 token
响应示例:
{
"idle": false,
"next_time": "2025-07-18T12:34:56+08:00"
}
返回值说明:
thread_status 状态 | 状态说明 | idle 字段值 | next_time 字段值 |
---|---|---|---|
STOPPED | 调度器已停止 | true | null |
IDLE | 调度器空闲等待中 | true | 下一个任务时间(非null) |
RUNNING | 调度器正在运行 | false | 下一个任务时间(非null) |
STOPPING | 调度器正在停止中 | false | 可能为非null(如果队列中还有任务)或 null(如果队列已空) |
2.3 实时日志流
WebSocket /ws
- 认证要求:无需 token
消息格式:
{
"type": "log",
"data": "2025-07-18 11:45:23 [INFO] 开始执行基建任务"
}