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_timeoutproxy_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_versionproxy_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 响应

AWS Lambda,API Gateway 返回 Malformed Lambda 代理响应,502 错误

如何在 NGINX 的代理响应中重写 URL

php中的长时间计算导致503错误

绕过 Elasticbeanstalk 中托管的 api 响应上的 NGINX 413 错误页面