真正健壮的服务健康检查需三重验证:优先调用健康接口(如curl -f --max-time 3),失败则查带唯一标识的进程(pgrep -f "app.name=myapi"),最后才查端口;重启前须 graceful shutdown、资源清理及权限适配,并动态配置检查频率与退避策略。

检查服务进程是否存在比单纯查端口更可靠
很多脚本直接用 netstat 或 ss 查端口是否监听,但服务可能卡在假死状态:端口开着,进程没响应。真正健壮的做法是结合进程名 + 端口 + 健康接口三重验证。比如对一个 HTTP 服务,优先尝试 curl -f -s -o /dev/null --max-time 3 http://127.0.0.1:8080/health;失败再 fallback 到查进程:pgrep -f "java.*myapp.jar";仍失败才考虑端口存活(nc -z 127.0.0.1 8080)。
注意:pgrep -f 容易误匹配,建议在启动命令中加唯一标识,如 java -Dapp.name=myapi -jar myapp.jar,然后用 pgrep -f "app.name=myapi" 精准定位。
重启前必须做 graceful shutdown 和资源清理
直接 kill -9 可能导致数据库连接未释放、临时文件残留、日志截断。正确流程是:
- 先发
SIGTERM(kill $(cat /var/run/myapp.pid)),等 5 秒 - 检查进程是否退出:
kill -0 $(cat /var/run/myapp.pid) 2>/dev/null;若仍存在,再发SIGKILL - 清空旧 pid 文件、释放绑定端口(避免重启失败)、删除 lock 文件(如有)
- 确认
/tmp或/var/tmp下无残留 socket 或临时目录
如果服务本身不支持 SIGTERM(如某些 Python 脚本),需在启动时用 exec 替换 shell 进程,或改用 systemd 管理并配置 KillMode=process。
健康检查频率和失败阈值不能硬编码成固定值
检查太频繁(如每 5 秒)会增加负载,太松(如每 5 分钟)又起不到保护作用。应按服务特性分级设置:
- 核心 API 服务:间隔 10–15 秒,连续 3 次失败才重启
- 后台任务型服务(如定时导出):间隔 60 秒,连续 2 次失败即告警,5 次才重启
- 使用环境变量或配置文件控制,例如读取
HEALTH_INTERVAL=${HEALTH_INTERVAL:-15}和RESTART_THRESHOLD=${RESTART_THRESHOLD:-3}
还要防雪崩:重启后立即检查可能失败,建议加入退避逻辑,比如第 1 次重启后等 5 秒再检查,第 2 次失败则等 15 秒,避免反复拉起又崩溃。
日志和状态记录必须写入独立文件,不能只依赖 stdout
后台运行的守护脚本(如用 nohup ./health.sh &)一旦 stdout/stderr 重定向丢失,所有输出就没了。必须显式记录:
- 每次检查结果写入
/var/log/myapp/health.log,带时间戳和 exit code - 重启动作记入
/var/log/myapp/restart.log,包含旧 PID、新 PID、触发原因(如 “curl timeout”) - 用
logger -t myapp-health同步推送到 syslog,便于集中采集 - 日志轮转要单独处理,比如用
logrotate配置,避免填满磁盘
最容易被忽略的是权限问题:脚本以普通用户运行,但日志路径(如 /var/log/myapp/)可能属 root。要么提前创建目录并设好 group+w,要么把日志写到用户可写的 $HOME/logs/ 下再软链过去。











