NSSM托管Python打包Windows服务无输出、运行约一天后异常停滞排查
服务长时间运行后静默停滞的潜在触发原因
结合你描述的现象(00:05执行正常完成后无任何日志、重启即恢复、排除系统休眠/网络异常),核心触发原因都和现有实现的缺陷、Windows服务运行环境的特性相关,具体如下:
- 无间隔空转循环导致的CPU调度饿死
你的核心循环while True没有添加任何sleep间隔,会让进程持续占用单个CPU核心跑满100%。刚启动时系统调度资源充足,循环逻辑能正常拿到执行权;运行约一天后,系统后台累积的临时任务、内存碎片增多,Windows会自动降低高占用、无交互的后台进程调度优先级,极端情况下进程会被降权到几乎拿不到CPU执行时间片,根本无法检测到后续的触发分钟点。这种僵死不会触发任何Python异常,自然不会产生错误日志。 - 标准输出缓冲区阻塞导致的静默卡死
代码逻辑里itcmodel执行完成后,先调用print再写日志、更新last_run值。Python在后台服务场景下,标准输出默认是全缓冲模式,不会实时把内容写出缓冲区。当NSSM把进程的标准输出重定向到文件/空设备时,缓冲区累积到阈值后,进程会直接阻塞在print的IO调用上——这时候因为代码还没执行到logging.info和last_run更新的步骤,不会留下任何日志记录,表现为执行完00:05的业务逻辑后彻底停滞。 - 第三方依赖的非托管内存泄漏触发进程挂起
加载模型依赖的第三方库(尤其是包含C/C++扩展的机器学习类库)很容易存在非托管内存泄漏,不会被Python的垃圾回收机制清理。运行约一天后,进程内存占用会涨到Windows给服务进程分配的内存配额阈值,系统不会直接崩溃进程,而是强制挂起进程的所有执行流,让它停止消耗资源。这种情况因为进程被系统直接挂起,程序根本没有机会写错误日志,重启后内存占用清零就会恢复正常。 - 触发逻辑的状态值边界bug
你用来判断是否重复执行的last_run只存储了分钟值(0-59),没有关联小时、日期信息。如果中间因为调度卡顿导致循环执行延后,很容易出现跨小时的分钟值重复匹配(比如00:05执行完存储last_run=5,循环卡顿到01:05才恢复检测,会因为分钟值都是5,判定为“已执行过”跳过触发),后续虽然会跳过几个触发点,但如果卡顿时间超过两个触发间隔,就会直接错过所有后续执行节点,表现为完全无日志输出。
对应的优化排查方向
- 给核心循环添加最小间隔的
sleep(比如time.sleep(30)),彻底消除CPU空转问题,避免被系统调度降权 - 所有
print调用添加flush=True参数,或者在NSSM配置里给进程设置PYTHONUNBUFFERED=1环境变量,从根源上避免输出缓冲区阻塞 - 把所有业务逻辑包裹在全局
try-except捕获块中,任何异常都实时写入日志文件,杜绝静默卡死的可能 - 修正触发逻辑:将
last_run从存储分钟值,改为存储精确到分钟的datetime时间戳,避免跨小时场景下分钟值重复匹配导致的逻辑误判 - 运行过程中通过任务管理器监控进程的CPU、内存占用趋势,定位是否存在第三方库导致的内存泄漏、CPU持续高占用问题
- 在NSSM配置中开启进程崩溃自动重启规则,设置内存占用阈值告警,降低异常停滞的影响
内容的提问来源于stack exchange,提问作者phoenix
相关产品推荐
相关产品推荐

