1. 服务假死问题的本质与监控方案选型
服务器进程假死是运维工作中最令人头疼的问题之一——进程仍在运行却不再响应请求。这种情况往往比彻底崩溃更难发现,因为系统监控工具通常只能检测进程是否存在,而无法判断其是否健康工作。
假死现象通常由以下原因导致:
- 线程死锁或资源竞争
- 未处理的异常阻塞主线程
- 内存泄漏导致响应缓慢
- 外部依赖服务超时未返回
- 文件描述符耗尽
我管理的一个Java Web服务就曾频繁出现假死:进程存活但HTTP端口完全无响应。传统的进程监控工具如systemd对此束手无策,这促使我研究出supervisor+cron的组合方案。
关键区别:崩溃(crash)是进程退出,假死(hang)是进程僵而不死。后者需要主动健康检查而非简单的进程存在性检查。
2. Supervisor基础配置与进程管理
Supervisor作为进程管理工具,其核心优势在于:
- 提供守护进程功能,崩溃后自动重启
- 统一的进程状态管理接口
- 详细的日志记录和事件通知
典型配置示例(/etc/supervisor/conf.d/webapp.conf):
[program:webapp] command=/usr/bin/java -jar /opt/webapp/webapp.jar directory=/opt/webapp user=webuser autostart=true autorestart=true startsecs=10 startretries=3 stderr_logfile=/var/log/webapp.err.log stdout_logfile=/var/log/webapp.out.log environment=JAVA_HOME="/usr/lib/jvm/java-11-openjdk"但默认配置只能处理进程崩溃场景。我们需要扩展其能力:
; 添加健康检查配置 killasgroup=true stopasgroup=true stopsignal=INT stopwaitsecs=603. 实现服务健康检查的Cron方案
健康检查脚本是整套方案的核心。我推荐使用curl进行HTTP服务检查,因为它:
- 支持连接超时和读取超时独立设置
- 能处理HTTPS证书验证
- 提供丰富的返回状态码
健康检查脚本示例(/opt/scripts/healthcheck.sh):
#!/bin/bash SERVICE_URL="http://localhost:8080/health" TIMEOUT=5 RETRY=3 for ((i=1; i<=$RETRY; i++)); do HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time $TIMEOUT $SERVICE_URL) if [ "$HTTP_CODE" -eq 200 ]; then exit 0 fi sleep 1 done # 所有重试失败后重启服务 supervisorctl restart webapp echo "$(date) - Restarted webapp due to health check failure" >> /var/log/service-monitor.log exit 1设置Cron任务(crontab -e):
*/5 * * * * /opt/scripts/healthcheck.sh >/dev/null 2>&14. 高级监控策略与性能优化
基础方案可以进一步优化:
多维度健康检查:
# 检查端口监听 ss -tulnp | grep ':8080' || supervisorctl restart webapp # 检查线程状态(Java应用) jstack $(pgrep -f webapp.jar) | grep -A10 "main" | grep "RUNNABLE" || supervisorctl restart webapp # 检查响应时间(超过2秒视为异常) RESP_TIME=$(curl -s -w "%{time_total}" -o /dev/null http://localhost:8080/health) [ $(echo "$RESP_TIME > 2" | bc) -eq 1 ] && supervisorctl restart webapp资源占用控制:
; 在supervisor配置中添加资源限制 [program:webapp] ... priority=500 process_name=%(program_name)s_%(process_num)02d numprocs=1 umask=022日志轮转配置(/etc/logrotate.d/webapp):
/var/log/webapp*.log { daily missingok rotate 30 compress delaycompress notifempty create 640 webuser adm sharedscripts postrotate /usr/bin/supervisorctl restart webapp >/dev/null 2>&1 || : endscript }5. 异常场景处理与实战经验
在实际部署中我遇到过几个典型问题:
假重启问题:有时supervisorctl restart命令看似执行成功,但实际进程PID未变。解决方案是:
supervisorctl stop webapp sleep 5 kill -9 $(pgrep -f webapp.jar) 2>/dev/null supervisorctl start webapp监控脚本自身挂起:添加执行超时控制:
timeout 30 /opt/scripts/healthcheck.sh || echo "Healthcheck timed out" >> /var/log/service-monitor.log服务启动顺序问题:依赖数据库的服务可能在DB就绪前启动失败。改进方案:
[program:webapp] startsecs=60 startretries=56. 监控系统的自我监控
任何监控系统都需要考虑自身可靠性。我采用的保障措施:
- 监控脚本监控:
0 * * * * [ -f /var/log/service-monitor.log ] || (echo "Monitor log missing!" | mail -s "Alert" admin@example.com)- Supervisor状态检查:
#!/bin/bash if ! supervisorctl status webapp >/dev/null; then systemctl restart supervisor echo "Restarted supervisor" | mail -s "Supervisor was down" admin@example.com fi- Cron任务验证:
# 检查最近一次执行时间 LAST_RUN=$(stat -c %Y /var/log/service-monitor.log) NOW=$(date +%s) [ $((NOW-LAST_RUN)) -gt 3600 ] && echo "Cron may not running!" | mail -s "Alert" admin@example.com这套方案在我管理的20多台服务器上稳定运行超过两年,将服务不可用时间降低了90%。关键在于定期审查监控日志并根据业务特点调整检查策略。比如电商系统在促销期间应该缩短检查间隔,而内部管理系统可以适当放宽标准以节省资源。