Django-nginx-gunicorn环境下视图加载超5秒触发504错误排查
你遇到的这个问题确实有点棘手——明明自己配置的超时都远大于5秒,但就是卡在5秒左右触发504,而且后台任务还能悄悄完成。结合你的django-nginx-gunicorn-supervisor-postgresql部署栈,我整理几个可能的方向和排查步骤:
问题回顾
当Django Admin里的视图(比如过滤大量记录的变更列表)加载耗时超过约5秒时,就会返回504错误;但记录少、加载快时完全正常。更奇怪的是,就算报了504,部分视图的操作还会在后台继续执行,最终能在数据库里看到修改结果。你已经调整了Nginx和Gunicorn的超时配置(都远大于5秒),但问题依旧。
你的现有配置如下:
Nginx 站点配置(/etc/nginx/sites-enabled/mysite)
proxy_connect_timeout 60; proxy_read_timeout 90; proxy_send_timeout 90; send_timeout 90; fastcgi_read_timeout 300;
Nginx 全局配置(/etc/nginx/nginx.conf)
send_timeout 300; keepalive_timeout 65; proxy_connect_timeout 300; proxy_read_timeout 300; proxy_send_timeout 300;
Gunicorn 启动脚本(supervisor管理的gunicorn_start)
exec gunicorn -c ${CONF_FILE} ${DJANGO_WSGI_MODULE}:application \ --name ${NAME} \ --user=${USER} --group=${GROUP} \ --log-level=debug \ --timeout=90
Gunicorn 配置文件(${CONF_FILE})
timeout = 90 graceful_timeout = 30 keepalive = 3
可能的原因分析
1. 上游代理/负载均衡的默认超时(最可能)
很多云服务商的负载均衡器、CDN或者服务器前置的其他反向代理,默认读取超时就是5秒。比如AWS ALB、Cloudflare、阿里云SLB这类服务,它们会先于你的Nginx触发超时,直接返回504给客户端,但你的后端Nginx和Gunicorn还在继续处理请求——这刚好能解释为什么后台任务能完成,但客户端收到了504。
2. PostgreSQL的查询超时
检查PostgreSQL的statement_timeout设置,这个参数会限制单个SQL语句的执行时间。如果它被设为5000毫秒(5秒),当你的过滤查询超时后,数据库会中断查询,但Django进程可能还在尝试处理后续逻辑,导致后台仍有操作残留。你可以登录PostgreSQL执行:
SELECT current_setting('statement_timeout');
默认值一般是0(无限制),如果返回5000,那就是这里的问题了。
3. Nginx隐藏的局部配置
虽然你全局和站点配置的超时都远大于5秒,但可以用nginx -T命令查看Nginx合并后的完整配置,确认有没有某个location块里被意外设置了proxy_read_timeout 5s这类配置——有时候复制粘贴配置或者第三方插件会引入这类隐藏限制。
4. Django中间件或第三方库的限制
有没有自定义的中间件在处理请求时设置了超时?或者使用了异步任务类的库(比如django-celery)但配置不当?另外,有些监控类的中间件(比如性能监控工具)可能会自带超时限制,也需要排查。
排查与解决步骤
- 定位504的来源:用浏览器开发者工具查看网络请求的响应头,或者用
curl -v https://your-domain/admin/your-long-view/请求超时的视图,看看504是由哪个服务返回的。如果响应头里有云服务商的标识(比如Cloudflare、AWS),那就是上游代理的问题,需要调整它们的超时设置。 - 检查PostgreSQL日志:查看PostgreSQL的日志(一般在
/var/log/postgresql/目录下),看看有没有查询超时的记录,对应statement_timeout的报错。如果有,修改postgresql.conf里的statement_timeout值(比如改成30000即30秒),然后重启PostgreSQL。 - 查看Nginx日志:检查
/var/log/nginx/error.log,如果日志里显示upstream timed out,那可能是Gunicorn这边的问题,但你已经设了90秒超时,所以可能性低;如果是gateway timeout且没有上游相关的提示,大概率是上游代理的问题。 - 验证Gunicorn的超时生效:可以在Django里加一个测试视图:
import time from django.http import HttpResponse def test_timeout_view(request): time.sleep(10) return HttpResponse("OK")
访问这个视图,如果10秒后返回OK,说明Gunicorn的超时没问题;如果5秒就返回504,那肯定是上游的问题。
内容的提问来源于stack exchange,提问作者klautern

