Flask Windows 服务化 6 小时强制重启实战: 上次 NSSM 救 11 天后 PRO 又死了

📅 2026-09-23 ⏱️ 7 分钟阅读 🏷️ DevOps ✍️ BaccAI Team
还记得 9/7 写的 W9 吗? 我说 schtasks + watchdog bat 让 Flask 跑了 11 天 528 小时没故障。

结果 9/19 用户反馈 "PRO 登录页打不开了", 我一查:PRO 进程死了, watchdog bat 文件也没了, schtasks 任务也丢了

11 天稳定运行的假象, 是 watchdog bat 早就被某次 Windows Defender 清扫时干掉了, 我又没察觉。9/23 我做了一件事: 不信任任何"自动"机制, 加了 6 小时强制重启兜底。三层守护: bat loop 死了秒拉起 + schtasks 6 小时强制 restart + 你手动也能一键重启。
9/7 到 9/23 Flask 服务化时间线
9/7 救场 → 9/19 静默死亡 → 9/23 三层兜底,16 天学到 3 件事
9/7 NSSM 失败 → schtasks + bat 救场
↓ 11 天稳定 (W9 文章里写的 "528 小时无故障")
9/19 PRO 静默死亡 (我没察觉)
↓ 用户反馈 + 排查
9/23 加 6 小时强制重启 + bat loop + 监控告警

一、上次以为搞定了, 其实没

9/7 那个下午我花 3.5 小时死磕 NSSM, 最后用 schtasks + watchdog bat 救场。写完 W9 文章的时候我还挺得意:"528 小时无故障"

结果 9/19 周日下午, 有用户邮件反馈 PRO 登录页打不开:

主题: PRO 登录不上
内容: 你好, https://www.baccpc.com:8000/login 这个页从昨天开始就打不开,
      一直转圈。我是 VIP 会员, 帮我看一下。

我 RDP 登上去一查:

netstat -an | findstr ":8000"
# 什么都没输出

Get-Process python
# 也没输出

PRO Flask 进程死了, 而且死了至少 24 小时。PHD 还活着 (9/23 早上还能用), 但 PRO 没了。

W9 文章里说的 "watchdog bat 死了自动重启", 前提是 bat 文件本身存在。9/13 ~ 9/19 之间某个时间点, watchdog bat 文件被 Windows Defender / 磁盘清理 / 我不记得的手动操作给删了。bat 没了, schtasks 任务就算运行也是 "找不到文件", Python 不会被重启。

我立刻检查 D:\phd824 和 D:\20260516\vb_bendi_v24 看 bat 文件还在不在:

Test-Path D:\phd824\run_phd_loop.bat
False

Test-Path D:\20260516\vb_bendi_v24\run_pro_loop.bat
False

两个 bat 都没了。watchdog bat 在 9/19 ~ 9/23 之间被人或系统清掉了, schtasks 任务虽然还在, 但执行的目标 bat 路径找不到, 所以服务死了也没人拉。

二、为什么 watchdog 救场不靠谱

W9 那套方案的逻辑是: Python 死了 → bat 检测到进程退出 → bat loop 回到 :restart → 杀所有 python → 重启 Python。

听起来完美, 但前提是 bat 自己活着。以下 3 种情况它会失效:

  1. bat 文件被删了: Windows Defender / 第三方杀毒 / 磁盘清理 / 手动误删
  2. bat 进程被杀: 杀软把 bat 当可疑脚本扫掉, 或者 cmd.exe 自身被回收
  3. schtasks 任务失效: 任务被禁用、删除、或者找不到 bat 路径

任何一种发生, 整套守护就失效。9/19 我遇到的是 1 (文件被清)。

三、6 小时强制重启: 不信任任何"自动"

9/23 我重新设计: 不依赖任何 bat 文件持续运行, 直接让操作系统层做定时 kill + 重启

思路很简单:

每一层独立工作, 任何一层失效, 其他两层兜底。

第 1 层: bat loop (桌面版本, 不放服务器)

我把 bat 放桌面, 不放 D:\ 根目录或 app 同级, 这样 Windows Defender 磁盘清理扫描 D:\ 时不会扫到:

桌面 `app.py -PRO.bat`:

@echo off
setlocal
title BaccAI PRO - 6h Auto Restart

cd /d D:\20260516\vb_bendi_v24

:loop
echo [%date% %time%] PRO watchdog started...
C:\Users\Administrator\AppData\Local\Programs\Python\Python38\python.exe app.py
goto :restart

:restart
echo [%date% %time%] PRO exited, killing all python and restarting in 10s...
timeout /t 10 /nobreak >nul
taskkill /F /IM python.exe /T 2>nul
timeout /t 5 /nobreak >nul
goto :loop

桌面 `app.py - PHD.bat` (同样逻辑, 路径换成 D:\phd824)。

双击桌面 bat, 会出现一个 cmd 窗口。最小化到任务栏即可, 不要关。Python 死了 bat 自动重启, 整个过程无需人工干预。

第 2 层: schtasks 6 小时强制 taskkill

Windows Task Scheduler 不需要 bat 持续运行, 它自己就是个 Windows 服务, 由系统维护。我配了 2 个 6 小时定时任务, 每 6 小时强制杀所有 python.exe:

schtasks /Create /TN "BaccAI_PRO_6h_restart" /TR "taskkill /F /IM python.exe /T" /SC HOURLY /MO 6 /RL HIGHEST /RU SYSTEM /F
schtasks /Create /TN "BaccAI_PHD_6h_restart" /TR "taskkill /F /IM python.exe /T" /SC HOURLY /MO 6 /RL HIGHEST /RU SYSTEM /F

参数解释:

关键设计: schtasks 本身不依赖任何 bat 文件, 它就是 Windows 服务, 永远不会被 Defender 清掉。taskkill 是 Windows 自带的 exe, 也不依赖 bat。

第 3 层: 服务器重启自动恢复

如果服务器本身重启 (Windows Update / 断电), 桌面的 bat 也会被关掉。我额外配了 2 个 ONSTART 任务, 开机 30 秒后自动跑桌面 bat:

schtasks /Create /TN "BaccAI_PRO_OnBoot" /TR "C:\Users\Administrator\Desktop\app.py -PRO.bat" /SC ONSTART /RL HIGHEST /RU SYSTEM /F
schtasks /Create /TN "BaccAI_PHD_OnBoot" /TR "C:\Users\Administrator\Desktop\app.py - PHD.bat" /SC ONSTART /RL HIGHEST /RU SYSTEM /F

但有个坑: ONSTART 跑 bat, bat 在 session 0 (系统服务会话) 里, 不会显示窗口, 也接受不到 stdin (如果要 Ctrl+C 杀进程会很麻烦)。不过我们不需要杀, 让它跑着就行。

三层守护架构图
三层守护: bat loop (秒级) + schtasks (小时级) + ONSTART (天级)

四、6 小时是最佳平衡点

为什么是 6 小时, 不是 1 小时也不是 24 小时?

间隔 好处 坏处 适合谁
1 小时 死了秒拉, 几乎察觉不到 一天重启 24 次, 用户登录态/表单数据会丢 内部工具, 不在意用户体验
6 小时 一天 4 次, 体验可接受 短任务可能撞上 ✅ 大部分 Web 服务, 推荐
24 小时 用户几乎感知不到 进程悄悄死掉, 24 小时没人拉 绝对不能中断的服务 (支付/医疗)

我选了 6 小时。理由:

6 小时是经过推敲的: 太短丢登录态, 太长救不回来悄悄死的进程。如果你的 Flask 用户访问频繁 (高频 API), 改成 2-4 小时; 如果是低频博客, 12-24 小时也行。

五、可能踩的坑 + 解决

坑 1: bat 窗口被误关

我同事点桌面 bat 时偶尔误关 cmd 窗口, watchdog 失效。解决:

坑 2: schtasks 任务被禁用

Windows Update 或者某些系统优化工具会把 schtasks 任务 "禁用" (Disabled) 状态。检查:

schtasks /Query /TN "BaccAI_PRO_6h_restart"

看 "状态" 列, 不是 "Ready" 就是有问题。

坑 3: taskkill 杀掉其他 Python 进程

我服务器只跑这两个 Flask, 所以 taskkill python.exe 全杀安全。如果有其他 Python 程序, 改用 window title 过滤:

taskkill /F /FI "WINDOWTITLE eq BaccAI*" /T

只杀标题含 BaccAI 的窗口。

坑 4: 模型加载慢, 6 小时重启太频繁丢用户

PRO LSTM 模型加载要 5-10 秒, 加数据库连接池初始化总共 15-20 秒。如果用户正好在用 PRO 算预测, 重启会丢。

短期妥协: 6 小时已经够短, 用户撞上的概率低。

长期方案: 上 Nginx 反向代理, 滚动重启 (kill 一个 worker 起一个新 worker), zero-downtime。但成本高, 等 W10+ 流量再上。

六、完整启动卡 (D:\seo\START_COMMANDS.txt 更新版)

Step 1: 状态检查

netstat -an | findstr ":8443  :8000  :8080"
Get-Process python | Select Id, StartTime
schtasks /Query /TN "BaccAI_PRO_6h_restart"
schtasks /Query /TN "BaccAI_PHD_6h_restart"

Step 2: 启动 (4 种方式任选)

:: 方式 1: 双击桌面 bat (推荐, 启动 + watchdog 自动重启)
:: C:\Users\Administrator\Desktop\app.py -PRO.bat
:: C:\Users\Administrator\Desktop\app.py - PHD.bat

:: 方式 2: Start-Process 后台跑
Start-Process "C:\Users\Administrator\Desktop\app.py -PRO.bat"
Start-Process "C:\Users\Administrator\Desktop\app.py - PHD.bat"

:: 方式 3: 手动单次跑 (调试用)
cd /d D:\phd824
C:\Users\Administrator\AppData\Local\Programs\Python\Python38\python.exe app.py

Step 3: 紧急重启 (1 命令)

taskkill /F /IM python.exe /T
timeout /t 5 /nobreak
Start-Process "C:\Users\Administrator\Desktop\app.py -PRO.bat"
Start-Process "C:\Users\Administrator\Desktop\app.py - PHD.bat"

Step 4: 6 小时重启任务 (首次部署)

schtasks /Create /TN "BaccAI_PRO_6h_restart" /TR "taskkill /F /IM python.exe /T" /SC HOURLY /MO 6 /RL HIGHEST /RU SYSTEM /F
schtasks /Create /TN "BaccAI_PHD_6h_restart" /TR "taskkill /F /IM python.exe /T" /SC HOURLY /MO 6 /RL HIGHEST /RU SYSTEM /F
schtasks /Create /TN "BaccAI_PRO_OnBoot" /TR "C:\Users\Administrator\Desktop\app.py -PRO.bat" /SC ONSTART /RL HIGHEST /RU SYSTEM /F
schtasks /Create /TN "BaccAI_PHD_OnBoot" /TR "C:\Users\Administrator\Desktop\app.py - PHD.bat" /SC ONSTART /RL HIGHEST /RU SYSTEM /F

七、给 HCU 修复期的教训

9/7 写 W9 的时候我以为找到了终极方案, 9/23 就被现实打脸。这其实是 SEO 修复期的一个隐喻:

  1. 不要相信任何"自动": watchdog bat 看着自动, 但文件被清就完蛋。Google 的算法看着自动, 但 6/30 一次就把我打回原形。
  2. 多层冗余永远对: 6/30 我发了 47 篇 spam, Google 降权。9/23 我加了 6 小时强制重启, 把"自动"替换成"强制 + 多层"。站群内容也一样, 不要依赖单一来源 (论坛 / 转载 / AI 生成), 要 PGC + UGC + 转载 + 视频 多层组合。
  3. 看不见的失败最致命: 9/19 PRO 死了 24 小时我没察觉, 因为没有告警。最终方案我加了健康检查邮件告警 (W9 文章里有), 跟 W10 的 6 小时强制重启一起, 任何时候服务挂了我都会收到通知。

常见问题 FAQ

为什么 watchdog bat 跑 11 天后 PRO 又死了?
watchdog bat 是死循环守护进程,理论上 Python 一死 bat 就重启。但实际有 3 种情况它救不回来: 1) watchdog bat 文件本身被系统清理(Windows Defender / 磁盘清理 / 误删),2) schtasks 任务因为某种原因 Stopped 了,3) Python 进程 hang 住不退出,bat 卡在 :loop 段无法到 :restart。9/23 我遇到的是 1+2 双重: bat 整个文件没了 + schtasks 任务也没了。
6 小时强制重启会不会太频繁?会不会丢用户请求?
6 小时是我权衡后的数字。1) 太短(1-2 小时)会频繁打断用户操作,丢登录态/表单数据,体验差。2) 太长(24 小时+)又救不回来悄悄死的进程。3) 6 小时 = 一天 4 次重启,每次停机 15-20 秒(taskkill + bat 等待 + Flask 启动 + 模型加载),绝大部分用户不会撞上,撞上刷新一下就行。我自己用了 2 天,没收到用户投诉。
taskkill /F /IM python.exe 会不会杀掉其他 Python 进程?
会。如果服务器上还跑了别的 Python 程序(数据爬虫 / 定时任务 / Jupyter 等),会被一起杀掉。我服务器只跑这两个 Flask,所以 taskkill python.exe 全杀安全。如果有别的 Python 进程,改用 taskkill /F /FI "WINDOWTITLE eq BaccAI*"
为什么不用 Flask 自带的 threaded=True 或者 waitress 替代方案?
Flask dev server 本身的并发能力是个问题,但根本问题是进程死亡。即使换成 waitress,内存泄漏 / 死锁 / 系统资源耗尽还是会死。所以方案分两层:1) Flask 本身用 waitress (生产 WSGI),2) 操作系统层用 6 小时强制重启兜底。两层都要做。
6 小时重启对模型加载慢的 Flask (LSTM / PyTorch) 怎么办?
PRO 的 LSTM 模型加载要 5-10 秒,加上数据库连接池初始化总共 15-20 秒。重启窗口期用户会看到 503。最优解:加 Nginx/IIS 反向代理,后端 Flask 在停机时反代自动返回 stale cache 或 '服务升级中,30 秒后回来' 提示页。或者用 waitress + 多 worker + 滚动重启 (kill 一个,起一个新的,再 kill 下一个),实现 zero-downtime。

📌 上次 NSSM 的故事还没看?

🛠️ W9: NSSM 4 失败 1 成功 🔐 W8: HTTPS 加密原理 📱 W6: 移动浏览器 SSL 修复