手头一个 Flask 应用,Gunicorn 跑在 8081 端口,用户直接访问 http://ip:8081/。页面加载很慢,有时候好几秒才出来。同一台服务器上另一个静态站点(Next.js 生成,Nginx 托管)响应就很快。
查了一圈:服务器负载正常、数据库正常、代码没性能瓶颈。最后发现问题出在访问路径上——用户走的 8081 端口 根本没经过 Nginx,所有请求直捅 Gunicorn。
把 8081 端口从 Gunicorn 手里拿过来交给 Nginx 监听,Gunicorn 改到内网 8083,问题解决了。首页加载从超时降到了 70 毫秒,CSS 文件从 232KB 变成 31KB(gzip)。
一张图说清楚架构变化
1 | 改前: |
为什么直连 Gunicorn 会慢
Gunicorn 是一个 WSGI 服务器。它的职责是跑 Python 代码,不是做静态文件分发或连接管理。但当用户直接访问 8081 端口时,Gunicorn 被迫干了三件它不擅长的事。
1. 静态文件要走一遍 Python 解释器
浏览器请求 bootstrap.min.css(232KB)。直连 Gunicorn 时:
1 | Nginx 处理:sendfile() 系统调用,零拷贝,直接从磁盘到网卡。 |
实测数据:
| 资源 | 大小 | Nginx 响应 | Gunicorn 响应 |
|---|---|---|---|
| bootstrap.min.css | 232KB | 0.27ms | 几十毫秒起(排队更久) |
| bootstrap-icons.min.css | 82KB | 0.3ms | 同上 |
| bootstrap.bundle.min.js | 80KB | 0.3ms | 同上 |
Gunicorn 的 worker 是 sync 模式,一个 woker 一次只能处理一个请求。如果有人在传整改报告、有人在查数据库,CSS 文件也得等。
2. 没有 gzip,传输量大了 5-7 倍
232KB 的 CSS,gzip 后只有 31KB。
Nginx 做 gzip:zlib(C 语言)处理,几毫秒,不阻塞其他请求。
Gunicorn 做 gzip:可以配,但每个请求都要 Python 跑一遍压缩,CPU 先上去,worker 更不够用。
开了 gzip 之后:
| 资源 | 原文大小 | gzip 后 | 节省 |
|---|---|---|---|
| bootstrap.min.css | 232KB | 31KB | 86% |
| bootstrap-icons.min.css | 82KB | 13KB | 84% |
| 页面 HTML | ~35KB | ~8KB | 77% |
对于移动端或网络状况差的用户,这几百 KB 的差距就是”等 5 秒”和”瞬间打开”的区别。
3. Sync worker 模式的排队问题
Gunicorn 配置了 6 个 sync worker。每个时间片只能处理一个请求。
当页面加载时,浏览器会同时发出多个请求(HTML + CSS + JS + 轮询接口)。如果 6 个 worker 都被占着,第 7 个请求就开始排队。
从 nginx 的访问日志里看到了很多 499 状态码——客户端超时断开连接时 nginx 记录的标志。在我测试 https://six-mah.yaoyoupei.com/inspection/ 时,这个 499 出现了好几次:curl 等了 10 秒没响应,自己关了。
改了什么
第一步:Gunicorn 改到内网端口
/data/projects/2025form/gunicorn.conf.py:
1 | # 改前:bind = "0.0.0.0:8081" |
第二步:Nginx 接管 8081 端口
在 six-mah.conf 里新增一个 server block,监听 8081:
1 | server { |
就这些。改完重启:
1 | sudo systemctl restart 2025form.service |
改造后的数据对比
| 测试项 | 直连 Gunicorn(改前) | Nginx → Gunicorn(改后) |
|---|---|---|
| 首页 / 加载 | 超时(>15s) | 70ms |
| CSS 文件(gzip 后) | 232KB 原文 | 31KB(gzip) |
| 静态文件响应 | 几十毫秒,还排队 | 0.3ms,不排队 |
| 首页完整渲染 | 用户体感 3-5 秒 | < 1 秒 |
什么时候用这种架构
Flask/Django 这类 Python Web 应用上线时,前面加一层 Nginx 不是可选项,是必选项。
不是因为 Nginx 有什么黑科技,原因很简单:
- Nginx 干它擅长的活:静态文件、gzip、SSL、连接管理
- Gunicorn 干它擅长的活:跑 Python 代码、查数据库、渲染动态页面
C 语言干 C 语言的事,Python 干 Python 的事。各司其职。
如果你的应用出现了:
- 页面加载慢但服务器负载不高
- 静态文件请求拖慢动态页面响应
- 客户端出现大量超时断开
先看看你的用户是不是直接连到了 Python 进程上。如果是,把这个活交给 Nginx。