Django部署后Nginx仅部分请求返回502 Bad Gateway问题排查
错误本质
upstream prematurely closed connection while reading response header from upstream 这条日志的核心含义是:Nginx等待Gunicorn返回响应头时,Gunicorn的连接突然中断。结合你描述的「1秒后报错、刷新即可恢复」的现象,问题并非单纯由数据量导致,而是数据量大的页面触发了超时或Gunicorn进程崩溃——哪怕你排除了图片数据,视图内的数据库查询、页面渲染耗时可能已超过默认超时限制,或是Gunicorn Worker因内存不足被系统杀死。
一步步排查修复
1. 调整Gunicorn超时与进程配置
Gunicorn默认Worker超时为30秒,若服务器内存不足,处理大页面时Worker可能因内存溢出(OOM)被系统杀死,或因超时被Gunicorn自身重启:
- 启动Gunicorn时显式设置更长超时,同时合理控制Worker数量(建议为
2*CPU核心数+1,避免超配占用过多内存):gunicorn --workers 2 --timeout 60 --log-level debug your_project.wsgi:application - 用
free -h查看服务器内存,若可用内存低于200M,直接升级Droplet配置(内存不足是硬伤); - 查看Gunicorn日志(若用systemd管理,执行
journalctl -u gunicorn),若出现worker exited with code 137,说明Worker被OOM Killer杀死,必须升级内存。
2. 修改Nginx上游超时设置
Nginx默认proxy_read_timeout为60秒,若Gunicorn处理页面耗时超过该阈值,Nginx会直接返回502。需在Nginx的Server配置块中添加/调整以下参数:
location / { proxy_pass http://unix:/var/run/your_project.sock; # 替换为你的Gunicorn Socket或IP:端口 proxy_read_timeout 60s; proxy_connect_timeout 60s; proxy_send_timeout 60s; # 以下请求头为Django识别请求必需,避免因头信息缺失导致异常 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }
修改完成后重启Nginx:sudo systemctl restart nginx
3. 排查Django视图的隐性耗时操作
即便排除了图片数据,视图内可能存在未优化的数据库查询(如N+1查询)、大量数据内存加载等耗时操作:
- 临时开启Django的
DEBUG=True(生产环境勿长期开启),用django.db.connection.queries打印所有数据库查询,排查重复慢查询,通过select_related/prefetch_related优化关联查询; - 例如视图中若存在
for project in Project.objects.all():后又调用project.images.all(),属于典型N+1问题,需改为Project.objects.all().prefetch_related('images')减少查询次数。
4. 确保静态文件由Nginx直接处理
本地存储的图片需由Nginx直接响应,避免占用Gunicorn Worker资源:
location /static/ { root /path/to/your/django/project; # 替换为你的项目根目录,如/var/www/portfolio expires 30d; # 开启静态文件缓存,降低重复请求压力 } location /media/ { root /path/to/your/django/project; expires 30d; }
总结
该问题是Nginx超时配置、Gunicorn进程稳定性、Django视图性能共同作用的结果,数据量大的页面只是触发条件。优先从调整超时配置、检查服务器内存入手,再优化视图查询逻辑,即可解决问题。
内容的提问来源于stack exchange,提问作者Bence Hargitai

