# 本地服务看门狗：配置陷阱与真验证法

适用：cron 每分钟兼底拉起本地页面/服务（如 `~/.hermes/scripts/learn_pages_watchdog.sh`）——用 MAP 表把「端口:子目录:根目录」映射到各服务的 `server.py`。

## 1. 改 MAP 时的字段陷阱（真实踩过）

解析通常是：`port="${row%%:*}"; rest="${row#*:}"; dir="${rest%%:*}"; base="${rest##*:}"`。

- 第二段必须是**相对根目录的子目录**，不能写人类可读的标签。写 `"8891:英语工作台BizLab:$BIZ"` 会去找 `$BIZ/英语工作台BizLab/server.py`，文件不存在 → 脚本打一行“跳过 xxx：server.py 不存在”就静默掠过去，**服务实际上无人托管**。正确写法：服务就在根目录时用 `"8891:.:$BIZ"`。
- 标签想显示给人看，就写在**端口号后面第三段**或另建一份“端口→名称”对照，不要塞进目录字段。

## 2. 「看进程还在」不等于被托管

错误验证：`ps ax | grep server.py` 有进程、`curl` 返回 200 → 以为没问题。那只说明**当下**活着；进程可能是你手工起的，看门狗其实一直在跳过。

正确验证（唯一有效）：
```bash
# 1. 杀掉端口上的进程
kill -9 $(lsof -nP -iTCP:8891 -sTCP:LISTEN -t)
sleep 2; lsof -nP -iTCP:8891 -sTCP:LISTEN -t   # 应为空
# 2. 跑一次脚本 或 等 cron 一分钟
bash ~/.hermes/scripts/learn_pages_watchdog.sh > /tmp/wd.log 2>&1 &
sleep 25
# 3. 确认端口又活了 + 看门狗日志有新行
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8891/
tail -2 ~/.hermes/logs/learn_watchdog.log
```

## 3. 测脚本时不要用管道接输出（否则命令挂死）

`bash 脚本 | sed 's/^/  /'` 会等**所有**持有 stdout 的子进程退出；脚本内部 `nohup ... &` 起来的服务若继承了 stdout，你的命令就永不返回，超时后工具会连**刚拉起的服务**一起杀掉（子进程同进程组）。

- 测活：`bash 脚本 > /tmp/wd.log 2>&1`，另起一条命令看日志
- 不要用 `bash -c '... 一长串命令 ...'` 包装：超时被杀时局面更难判断
- 汇报进度以**端口监听状态 + 日志行**为凭据，不以“我跑了脚本”为凭据

## 4. 设计约定
- 看门狗自己只做“端口没监听就拉起”，不上报、不重启正在跑的进程
- 服务把请求日志写进同一个日志文件，会让日志混入访问行；排查时先 `grep 已拉起`
