在某个客户的环境中,基于 Twisted 框架封装的 HTTP 接口在处理 Web 请求时表现出忽快忽慢的情况。客户反馈的一个具体问题是,一个页面上大约显示九个图表时,部分图表在加载时延迟明显,有时需要等待很长时间才能返回结果。虽然并发量和数据量不大,但在几天的运行后,性能却异常低下。
问题现象
项目在本地使用了三个 Twisted 进程,分别绑定到 127.0.0.1:8001
、127.0.0.1:8002
和 127.0.0.1:8003
。使用 Nginx 进行反向代理时,查看 Nginx 的日志发现,许多接口的响应时间超过 60 秒,有的甚至超过 100 秒。观察到这种性能问题具有规律性:所有响应缓慢的请求均指向 127.0.0.1:8003
。
疑点初步排查
最初,考虑到系统中存在许多定时任务,这些任务可能导致性能下降。然而,进一步分析后发现,慢请求与定时任务并无直接关联。由于客户环境限制,无法逐一暂停定时任务进行排查,原有的排查思路难以继续推进。
经过仔细检查 Nginx 的日志,发现所有慢请求均由 8003
处理。进而怀疑该进程是否开启了其他两个进程没有的定时任务配置,但配置文件内容检查显示三者配置相同。去掉 8003
的 upstream 后,服务恢复正常。
性能瓶颈调查
使用 strace
工具监控该进程,发现其不断重复发送类似日志的内容,并在遇到某个文件不存在的报错后,持续进入 sendto 状态,最终导致进程几乎瘫痪。该问题显然与 Twisted 框架中的 loggingobserver
机制有关。
在 Twisted 框架中,服务启动时会启动一个 loggingobserver
,用于同时记录 Python 和 Twisted 的日志到同一个文件。此时,发现使用的 handler 是带有轮转功能的(rotating),而 strace
输出的错误信息显示,进程在处理日志时发生了递归死循环,导致 CPU 负载剧增。
根本原因定位
进一步查看 Twisted 框架代码,发现 logging
模块在处理日志轮转时,未进行文件存在性的检查,导致在删除文件时出现错误,最终引发了死循环。虽然在 GitHub 上查找了 CPython 的日志代码,发现逻辑上没有问题,但正式环境中使用的 Python 版本为 2.7.5,与最新版本 2.7.14 存在差异,后者在处理文件重命名时添加了存在性检查,修复了这一问题(见 Python Bug Tracker)。
解决方案
将 Python 环境升级至最新版本,即可解决此 bug,恢复系统的正常性能。尽管未找到引发递归死循环的具体原因,但可推测是由于框架特定实现和 Twisted 机制的结合所致。
小结
这次故障的解决过程提醒我们,在使用第三方框架时,保持代码的更新与维护至关重要,及时跟进已知问题和修复措施,有助于避免潜在的性能瓶颈和系统崩溃。对于系统中的定时任务和其他进程,也应定期进行性能监测,以确保服务的稳定性。