uWSGI + Django 稳定性 CPU 抖动问题排查记录
问题现象
在稳定性测试中,一个使用 uWSGI 发布的 Django 应用出现了周期性 CPU 抖动。从监控图表可以看出,每大约 4 小时出现一次明显的 CPU 使用峰值,同时伴随内存下降:

初步分析:归因于 GC 的误区
第一直觉:垃圾回收(GC)
- 观察现象:CPU 峰值时内存下降,看起来像是 GC 触发
- 时间规律:每约 4 小时一次,呈强周期性
- 排查方向:查找是否有定时 GC(未发现)
怀疑方向:所有 worker 同时 GC
通过检索得知 uWSGI 的 pre-fork 模式可能导致:
- 所有 worker 共享相同 GC 配置
- 因为处理请求节奏类似,会同步触发 GC
- 大规模同时 GC → CPU 突刺
实施与验证
尝试方案 1:随机化 GC 阈值
import random
import gc
from uwsgidecorators import postfork
@postfork
def randomize_gc_threshold():
"""每个 worker 设置不同的 GC 阈值,避免同时 GC"""
gc.set_threshold(
random.randint(700, 900), # generation 0
random.randint(8, 12), # generation 1
random.randint(8, 12) # generation 2
)
结果:CPU 抖动未改善。
尝试方案 2:专用 GC 线程
原计划创建独立线程执行周期性 GC,但在进一步检索中发现关键线索……
关键突破:重新审视 uWSGI 配置
多处资料推荐如下配置:
max-requests = 5000
max-requests-delta = 300
然而实际项目配置为:
max-requests = 50000 # 处理 50000 个请求后重启 worker
计算验证
根据测试环境 TPS(约 3.5 req/s)计算:
重启间隔 = 50000 / 3 ≈ 16666.7 秒 ≈ 4.6 小时
完美吻合监控图中的 4 小时左右的 CPU 峰值!
CPU 抖动根本不是 GC,而是 worker 重启周期。
真相大白:worker 周期重启导致 CPU 峰值
工作机制
- worker 处理
max-requests数量后触发优雅重启 - 重启过程中会:
- 释放 Python 解释器内存
- 重新加载 Django 应用
- 重建连接池
- 重新导入模块
内存下降原因
不是 GC,而是:
- 老 worker 退出
- OS 完整回收该进程内存
- 新 worker 启动重新分配内存
优化建议
找到了问题点,那么解决起来就好说了,按照GPT给的说法,如果我们将重启点分散,那么这个CPU的凸起会有改善。于是用了一下调整测试了
调整重启策略(当前采用)
max-requests = 10000
max-requests-delta = 300
优点:
- 避免单 worker 堆积大量状态导致重启成本过大
- 随机偏移避免同时重启
- CPU 与内存曲线更平稳
基于内存的重启触发
reload-on-rss = 512 # RSS > 512MB 则重启
reload-on-as = 768 # 虚拟空间 > 768MB 则重启
再次出现的问题
测试后并未看到明显“随机效果”。开始怀疑 max-requests-delta 是否被支持。
执行验证:
uwsgi --help | grep max-requests-delta
未找到该参数。
进一步搜索
uwsgi --help | grep delta
输出为:
--max-worker-lifetime-delta add (worker_id * delta) seconds to the max_worker_lifetime value of each worker
说明当前版本仅支持:
--max-worker-lifetime-delta
并且该机制是 按 worker_id 线性偏移,而非随机偏移。
经验教训总结
排查误区反思
- 现象归因偏差
- 内存下降 ≠ GC
- 周期性 ≠ 定时任务
- 必须考虑所有相关机制
- 配置忽视
- 过度关注代码
- 忽略 runtime 配置
- 中间件的生命周期管理影响深远
- AI 给出的答案需要验证
- 多模型交叉校验
- 核对官方文档
- 版本差异非常关键
配置审计清单
uWSGI 关键配置:
max-requestsmax-worker-lifetimereload-on-rssharakirienable-metrics
后续
uWSGI 的重启机制是 CPU 抖动根源,但真正 CPU 占用高的地方是 Django 应用加载过程。 后续可进一步减少 worker 数量或优化 Django 启动速度。
核心收获
- 基础设施配置等同于代码质量的重要性
- 理解中间件生命周期是排障关键
- 建立系统化排查思维模型
- 监控结合日志才是完整视角
这次排查提醒我们:在复杂系统中,看似显而易见的原因往往只是错觉。真正的调优来自理解每一层组件的运行机制,而不是停留在表象。