nginx 代理在响应时间过长时导致错误
Posted
技术标签:
【中文标题】nginx 代理在响应时间过长时导致错误【英文标题】:nginx proxy causing error when response takes too long to reply 【发布时间】:2018-07-17 12:01:18 【问题描述】:我有一个 nginx 配置,它重定向到 Django 休息服务(通过 gunicorn)。
一切正常,但是当响应太大(响应时间超过 30 秒)时,我收到 503 服务不可用错误。 我确信这是因为这个问题,因为它在其他请求上正常工作,并且仅在响应太大(并从第三方 api 获取请求)的特定请求上花费太长时间。
下面是我的nginx配置:
server
listen www.server.com:80;
server_name www.server.com;
client_max_body_size 200M;
keepalive_timeout 300;
location /server/
proxy_pass http://127.0.0.1:8000/;
proxy_connect_timeout 120s;
proxy_read_timeout 300s;
client_max_body_size 200M;
location /
root /var/www/html;
index index.html index.htm;
我确定问题出在 Nginx 而不是 gunicorn,因为如果我从机器内部进行 curl 我会得到响应。
谢谢,
【问题讨论】:
请发布您的NGINX error log 的输出。还要在/var/log/syslog
中发布这些错误之一的输出(如果有)以及您的 Gunicorn 应用程序的错误日志。什么是服务器设置以及您正在运行的浏览器是什么(假设这是一个网络请求)?
这就是问题所在,两边都有 n 错误,并且从应用程序中它实际上正在返回结果(虽然晚了)
如果应用返回的是正确的结果,没有报错,但是很慢,是不是第三方API服务器响应太慢的问题?我不确定要解决的问题是什么。 NGINX 访问日志中有条目吗?您是说 NGINX 在其访问日志中显示 503 错误但在错误日志中没有任何内容?
可能是 django 应用程序失败了?似乎是这样。
@DanyY,看看我的回答是否有帮助
【参考方案1】:
当你在下面跑时
$ gunicorn --help | grep -A2 -i time
--graceful-timeout INT
Timeout for graceful workers restart. [30]
--do-handshake-on-connect
Whether to perform SSL handshake on socket connect
--
-t INT, --timeout INT
Workers silent for more than this many seconds are
killed and restarted. [30]
所以我假设超时发生在gunicorn
而不是通过 nginx。所以你不仅需要在 nginx 端增加超时时间,还需要在gunicorn
你可以添加
timeout=180
添加到您的 config.py
文件中,或者您可以在启动 gunicorn
时将其添加到命令行中
gunicorn -t 180 ......
【讨论】:
感谢您的回答,我实际上考虑到了这一点,并尝试增加超时(与 nginx 相同的值)并尝试添加工人,但仍然面临同样的问题 出现错误时能否贴出你的nginx访问日志和错误日志? 没有错误,就是问题所在。 error.log 保持为空,access.log 中没有添加行 你前面有没有使用负载均衡器?还是某个服务器或者IP直接打到nginx? 没有IP直接打到nginx【参考方案2】:您确实指定了proxy_connect_timeout
和proxy_read_timeout
,但从未指定proxy_send_timeout
。 (TBH,我认为您不需要修改connect(2)
的超时时间,因为该调用只是建立了 TCP 连接,并且不依赖于单个页面的大小或时间;但其他两个似乎是公平的游戏。)
另外,根据https://***.com/a/48614613/1122270,另一个考虑因素可能是proxy_http_version
——你的curl
可能使用HTTP/1.1
,而nginx默认使用HTTP/1.0
,你的后端可能会有不同的行为。
【讨论】:
我添加了proxy_http_version
和proxy_send_timeout
但我仍然面临这个问题,知道吗?
可能会通过tcpdump 调查问题以查看错误发生的位置。你在做proxy_pass的缓存吗?禁用nginx.org/r/proxy_buffering 可能会有所帮助,这可以针对特定位置内的这些长期存在的请求完成,也可以通过 HTTP 标头完成。顺便说一句,长期请求的整个想法通常没有实际意义——为什么有人需要等待超过 30 秒才能完成您的请求?!
我会尝试 tcpdump 看看发生了什么,这 30 秒只是因为我正在使用后端的旅行预订 api,他们有时需要很长时间才能响应,你认为 keepalive_timeout 是在这种情况下有什么用?
我怀疑这可能与proxy_buffering
有关,那么。如果您的后端使用的外部 API 确认预订的速度太慢,通常最好的做法是简单地向客户端发送几个字节,直到可以加载整个页面(这需要在 nginx 中关闭 proxy_buffering
才能工作适当地);或者,另一种好方法是让页面每 10 秒刷新一次,返回到自身(仅使用 HTML http-equiv 刷新),直到后端得到确认,即当您显示正确的页面而不是刷新时占位符。
谢谢,这似乎是最可靠的方法。当您说best practice to simply send a few bytes to the client until the whole page can be loaded
时,您的意思是手动将数据存储在数据库中并再次调用以取回它们?以上是关于nginx 代理在响应时间过长时导致错误的主要内容,如果未能解决你的问题,请参考以下文章
在 nginx 代理后面使用 docker 私有注册表 (v2) 的 HTTP 响应格式错误
DNS 和 nginx 服务器设置导致服务器缓慢和 502 响应