NGINX 499错误码的可能原因

我得到了很多499 NGINX错误码。我知道这是客户端的问题。这不是NGINX或我的uWSGI堆栈的问题。当a得到499时,我注意到uWSGI日志中的相关性。

address space usage: 383692800 bytes/365MB} {rss usage: 167038976
bytes/159MB} [pid: 16614|app: 0|req: 74184/222373] 74.125.191.16 ()
{36 vars in 481 bytes} [Fri Oct 19 10:07:07 2012] POST /bidder/ =>
generated 0 bytes in 8 msecs (HTTP/1.1 200) 1 headers in 59 bytes (1
switches on core 1760)
SIGPIPE: writing to a closed pipe/socket/fd (probably the client
disconnected) on request /bidder/ (ip 74.125.xxx.xxx) !!!
Fri Oct 19 10:07:07 2012 - write(): Broken pipe [proto/uwsgi.c line
143] during POST /bidder/ (74.125.xxx.xxx)
IOError: write error

我正在寻找一个更深入的解释,希望我的NGINX配置uwsgi没有问题。我只看表面。好像是客户的问题。

458136 次浏览

HTTP 499在Nginx中意味着服务器响应请求之前的客户端关闭连接。根据我的经验通常是由客户端超时引起的。据我所知,这是Nginx特定的错误代码。

就我而言,我没有耐心,最终误解了日志。

事实上,真正的问题是nginx和uwsgi之间的通信,而不是浏览器和nginx之间的通信。如果我在浏览器中加载了这个网站,并且等了足够长的时间,我就会得到一个“504 -坏网关”。但花了很长时间,我一直在尝试,然后在浏览器中刷新。所以我没有等待足够长的时间来看到504错误。当在浏览器中刷新时,也就是关闭前一个请求时,Nginx将其写入日志为499。

细化

在这里,我假设读者知道的和我刚开始玩游戏时一样少。

我的设置是一个反向代理,nginx服务器,和一个应用服务器,后面是uWSGI服务器。来自客户端的所有请求都将发送到nginx服务器,然后转发到uWSGI服务器,然后以同样的方式返回响应。我认为这是每个人使用nginx/uwsgi和应该使用它的方式。

我的nginx正常工作,但是uwsgi服务器出了问题。uwsgi服务器无法响应nginx服务器有两种(也许更多)方式。

1) uWSGI说:“我正在处理,请稍等,您很快就会得到回复”。Nginx有一段时间,它愿意等待,fx 20秒。之后,它将响应客户端,并返回一个504错误。

2) uWSGI死了,或者在nginx等待它的时候uWSGI死了。Nginx马上就看到了,在这种情况下,它返回一个499错误。

我通过在客户端(浏览器)中发出请求来测试我的设置。在浏览器中什么都没有发生,它只是一直挂着。大约10秒钟之后(比超时时间还短),我得出结论,有些地方不太对(这是真的),并从命令行关闭uWSGI服务器。然后我将转到uWSGI设置,尝试一些新的设置,然后重新启动uWSGI服务器。当我关闭uWSGI服务器时,nginx服务器将返回一个499错误。

所以我一直在调试499错误,这意味着在谷歌上搜索499错误。但是如果我等了足够长的时间,就会得到504错误。如果我得到504错误,我就能够更好地理解问题,然后能够调试。

所以结论是,问题出在uWGSI上,它一直挂着(“再等一会儿,再等一会儿,然后我就会给你一个答案……”)。

我是如何修复问题的,我不记得了。我想这可能是由很多事情引起的。

一旦我得到防病毒软件禁止请求作为AJAX http响应(卡巴斯基互联网安全与轻启发式分析的假阳性,深度启发式分析正确地知道没有任何错误)。

...从谷歌搜索过来的

我在这里找到了答案——> https://stackoverflow.com/a/15621223/1093174

这是为了提高我的AWS弹性负载均衡器的连接空闲超时!

(我用nginx/apache反向代理设置了一个Django站点,一个非常非常非常日志后端作业/视图超时)

出现这种行为的原因之一可能是你在uwsgi中使用了http而不是socket。如果直接使用uwsgi,请使用下面的命令。

uwsgi --socket :8080 --module app-name.wsgi

ini文件中的相同命令是

chdir = /path/to/app/folder
socket = :8080
module = app-name.wsgi

我遇到了这个问题,原因是由于浏览器上的卡巴斯基保护插件。如果你遇到这种情况,试着禁用你的插件,看看这是否能解决你的问题。

“client"在“客户端关闭连接”中;并不一定是Web浏览器!

如果你在你的用户和你的Nginx之间有一个负载平衡服务——使用AWS或haproxy,你可能会在Nginx日志文件中发现499个错误。在这个配置中,负载均衡器服务将充当Nginx服务器的客户端和Web浏览器的服务器,来回代理数据。

对于haproxy,某些适用的超时默认值为连接到上游和从上游(Nginx)或下游(Web浏览器)读取约60秒。

这意味着如果代理在大约60秒后还没有连接到上游进行写入,或者如果它还没有分别从下游(Web浏览器)或上游(Nginx)接收到任何数据作为HTTP请求或响应的一部分,它将关闭相应的连接,这将被Nginx视为错误,至少,如果后者当时正在处理请求(花了太长时间)。

对于繁忙的网站或需要更多时间执行的脚本,可能会发生超时。您可能需要找到一个适合您的超时值。例如,将它扩展到更大的数字,比如180秒。那也许能帮你解决问题。

根据你的设置,你可能会在浏览器中看到一个504 Gateway Timeout HTTP错误,这可能表明php-fpm有问题。但是,如果日志文件中有499个错误,情况就不是这样了。

使用标准的nginx配置和php-fpm,这个错误很容易重现。

在页面上按下F5按钮将向服务器创建数十个刷新请求。浏览器在刷新时取消之前的每个请求。以我为例,我在客户的网上商店日志文件中发现了数十个499。从nginx的角度来看:如果在下一次刷新请求之前没有将响应传递给客户端,nginx将记录499错误。

mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:32 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:33 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:34 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:35 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)
mydomain.com.log:84.240.77.112 - - [19/Jun/2018:09:07:35 +0200] "GET /(path) HTTP/2.0" 499 0 "-" (user-agent-string)

当然,如果php-fpm处理需要更长的时间(比如一个沉重的WP页面),它可能会导致问题。例如,我听说过php-fpm崩溃,但我相信可以通过正确配置服务来防止崩溃,比如处理对xmlrpc.php的调用。

在我的情况下,当客户端API在得到任何响应之前关闭连接时,我得到了499。字面上发送了一个帖子,并立即关闭连接。 这是通过选项解决的:

proxy_ignore_client_abort上

Nginx doc

当你指向499时,nginx记录了一个连接中止。但这通常是在后端服务器太慢时产生的,另一个代理先超时或用户软件中止连接。因此,检查uWSGI是否响应快,或者uWSGI /数据库服务器上是否有负载。

在很多情况下,在用户和nginx之间有一些其他的代理。有些可能在你的基础设施中,比如CDN,负载均衡器,清漆缓存等。其他可以在用户端,如缓存代理等。

如果你这边有代理,比如LoadBalancer / CDN……您应该将超时设置为先超时您的后端,然后逐步将其他代理超时给用户。

如果你有:

user >>> CDN >>> Load Balancer >>> Nginx >>> uWSGI

我建议你设置:

  • n秒到uWSGI超时
  • n+1秒到nginx超时
  • n+2秒超时到负载均衡器
  • n+3秒超时到CDN。

如果你不能设置一些超时(比如CDN),找到它的超时是什么,并根据它调整其他的超时(nn-1…)

这提供了一个正确的超时链。你会发现是谁给出了超时并将正确的响应代码返回给用户。

事实证明499确实意味着“客户端连接中断”。

我有一个客户“阅读超时”;设置为60s (nginx也有默认的proxy_read_timeout为60s)。所以在我的例子中发生的是nginx会error.log一个upstream timed out (110: Connection timed out) while reading upstream,然后nginx会重试“你配置的后端服务器组中的下一个代理服务器”。如果你有不止一个。

然后它尝试下一个,下一个,直到(通过默认的)它耗尽了所有的。当每个人超时时,它会将他们从“live”列表中删除;后端服务器也是如此。在全部耗尽后,它返回504 gateway timeout.

所以在我的例子中,nginx将服务器标记为“不可用”,在下一个服务器上重新尝试它,然后我的客户端60s超时(立即)发生,所以我将看到upstream timed out (110: Connection timed out) while reading upstream日志,紧接着是499日志。但这只是时间上的巧合。

相关:

如果组中的所有服务器都被标记为当前不可用,那么它也会在10s内返回502 Bad Gateway.。参见在这里 max_fails和fail_timeout。在日志中,它会说no live upstreams while connecting to upstream.

如果你的服务器组中只有一个代理后端,它只尝试使用一个服务器,并返回504 Gateway Time-out,并且不会从“live”列表中删除该服务器。服务器,如果proxy_read_timeout被超越。如果一个组中只有一台服务器,max_fails, fail_timeout和slow_start参数将被忽略,这样的服务器将永远不会被认为是不可用的。

真正棘手的部分是,如果您将proxy_pass指定为“;localhost"你的电脑碰巧也有ipv6和ipv4版本的localhost。与此同时(大多数盒子默认是这样),它将被视为你有一个“列表”;服务器组中的多个服务器,这意味着您可以进入上面的情况,让它返回"502 for 10 "即使你只列出一个服务器。参见在这里“如果一个域名解析为几个地址,所有的地址都将以循环的方式使用。” 一个解决方法是将它声明为proxy_pass http://127.0.0.1:5001;(它的ipv4地址)到避免,它既是ipv6又是ipv4。那么它就算作“只有一台服务器”。行为。< / p >

这里有一些不同的设置,你可以调整使它“少”;一个问题。比如增加超时时间或使其不会将服务器标记为“禁用”;当他们暂停的时候……或者修复列表,使它只有大小1,见上面:)

参见:https://serverfault.com/a/783624/27813

这并没有回答OPs的问题,但由于我在激烈地寻找答案后来到这里,我想分享我们的发现。

在我们的例子中,这些499是意料之中的。例如,当用户在某些搜索框中使用提前输入功能时,我们会在日志中看到类似的内容。

GET /api/search?q=h [Status 499]
GET /api/search?q=he [Status 499]
GET /api/search?q=hel [Status 499]
GET /api/search?q=hell [Status 499]
GET /api/search?q=hello [Status 200]

所以在我们的情况下,我认为使用proxy_ignore_client_abort on是安全的,这是在以前的回答中建议的。谢谢你!

对我来说,我已经启用了ufw,但我忘记暴露我的上行端口。_。

在我的例子中,我有这样的设置

AWS ELB >> ECS(nginx) >> ECS(php-fpm).
我为ECS(php-fpm)服务配置了错误的AWS安全组,因此Nginx无法接触到php-fpm任务容器。 这就是为什么我在nginx任务日志

中得到错误
499 0 - elb-healthchecker/2.0

健康检查配置为检查php-fpm服务并确认其正常运行并返回响应。

我知道这是一个旧线程,但它完全符合最近发生在我身上的事情,我想我应该在这里记录它。设置(在Docker中)如下:

  • nginx_proxy
  • nginx
  • Php_fpm运行实际的应用程序。

该症状是“502网关超时”;在应用程序登录提示符上。检查日志发现:

  • 该按钮通过HTTP的POST/login…所以……
  • nginx-proxy得到/login请求,并最终报告超时。
  • nginx返回499响应,这当然意味着“主机死亡!”
  • FPM服务器日志中的/login请求根本没有出现! !
  • 在FPM中没有回溯或错误消息…没有,零,zippo,零。

结果发现,问题是无法连接到数据库以验证登录。但是如何计算出来,结果是纯粹的猜测。

完全没有应用程序回溯日志…甚至没有FPM收到请求的记录…对我来说是一个完全的惊喜。是的,应用程序应该记录失败,但在这种情况下,它看起来像FPM工作进程死于运行时错误,导致nginx的499响应。现在,这显然是我们应用程序中的一个问题……在某处。但我想记录下发生的事情的细节,以帮助下一个面临类似问题的人。

我们在生产中也得到了499响应代码。我们的堆栈是

  1. NGINX,
  2. Gunicorn
  3. Django
  4. 芹菜(异步)
  5. Redis芹菜经纪人。
  6. Postgresql

< >强的问题: 我们的API没有返回响应到Gunicorn >NGINX。因为Redis关闭了(正在加载数据),芹菜将请求传递给.delay()方法来从API卸载工作负载,它没有返回任何响应


如何在Django和其他堆栈中重现它?

不从API返回任何响应。NGINX将向客户端发送499响应码。

我们是怎么解决的?

我们检查了堆栈的每个组件,最终找到了一个导致组件,它是Redis。我们注释了.delay()(此方法使用Redis)方法调用并测试了API,它工作正常。

这可能是NGINX返回499的原因之一。 确保您的Web框架是否返回响应。如果返回200,则检查NGINX配置或客户端