Nginx给uWSGI提供的请求很老?

5
我看到一个奇怪的情况,其中Nginx或uwsgi似乎正在建立一个长时间的请求队列,并在客户端连接超时后长时间尝试处理它们。我想了解并停止这种行为。以下是更多信息:
我的服务器使用Nginx通过Unix文件套接字将HTTPS POST请求传递给uWSGI和Flask。我基本上在所有东西上都使用默认配置。 我有一个Python客户端向服务器发送每秒钟3个请求。
在运行客户端约4小时后,客户端机器开始报告所有连接超时。(它使用Python请求库,并设置了7秒超时。)大约10分钟后,行为发生了变化:连接开始以502 Bad Gateway失败。
我关闭了客户端。但在关闭客户端后约10分钟,服务器端的uWSGI日志显示uWSGI仍然尝试响应来自该客户端的请求!而且top显示uWSGI使用100% CPU(每个工作进程使用25%)。
在那10分钟内,每个uwsgi.log条目都像这样: Thu May 25 07:36:37 2017 - SIGPIPE: writing to a closed pipe/socket/fd (probably the client disconnected) on request /api/polldata (ip 98.210.18.212) !!! Thu May 25 07:36:37 2017 - uwsgi_response_writev_headers_and_body_do(): Broken pipe [core/writer.c line 296] during POST /api/polldata (98.210.18.212) IOError: write error [pid: 34|app: 0|req: 645/12472] 98.210.18.212 () {42 vars in 588 bytes} [Thu May 25 07:36:08 2017] POST /api/polldata => generated 0 bytes in 28345 msecs (HTTP/1.1 200) 2 headers in 0 bytes (0 switches on core 0)
Nginx的error.log显示了很多这样的内容: 2017/05/25 08:10:29 [error] 36#36: *35037 connect() to unix:/srv/my_server/myproject.sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: 98.210.18.212, server: example.com, request: "POST /api/polldata HTTP/1.1", upstream: "uwsgi://unix:/srv/my_server/myproject.sock:", host: "example.com:5000"
大约10分钟后,uWSGI活动停止。当我重新启动客户端时,Nginx愉快地接受POST请求,但是uWSGI在每个请求上都会给出相同的“写入关闭的管道”错误,就好像它被永久性地破坏了一样。重启web服务器的docker容器无法解决问题,但重新启动主机可以解决问题。
理论:
在默认的Nginx-> socket-> uWSGI配置中,是否存在没有超时的请求长队列?我查看了uWSGI文档,并看到了一堆可配置的超时时间,但所有默认值都约为60秒左右,因此我无法理解为什么会处理10分钟前的请求。我没有更改任何默认的超时设置。
该应用程序在我的小型开发服务器上使用了几乎所有的1GB RAM,因此我认为资源限制可能会触发这种行为。
无论如何,我想更改我的配置,以便> 30秒的请求会因500错误而被丢弃,而不是被uWSGI处理。我希望能得到关于如何做到这一点的建议和对所发生情况的理论。
2个回答

4
这似乎是uWSGI方面的问题。
听起来你的后端代码可能存在问题,即处理请求太长时间,没有实现任何类型的请求速率限制,并且没有正确地捕获底层连接是否已终止(因此,您会收到错误消息,指出您的代码试图写入关闭的管道,甚至在底层连接已经终止之后,可能仍在开始处理新的请求)。

1
谢谢,这些解释很有道理——基本上nginx正在使用请求填充uwsgi的套接字,而忽略了uwsgi没有处理它们的事实。默认配置允许这样做很奇怪;我原以为uwsgi至少会根据传入请求在套接字中停留的时间设置超时。ignore_client_abort设置很有趣;我会尝试一下。(harakiri设置并没有解决问题。) - Luke
@Luke,是的,我认为,基本上,uWSGI做出了一个保守的假设,即使客户端立即断开连接,您可能不想丢弃任何请求;最起码,当您开始处理请求时,您可能希望修改后端以至少检查客户端仍然连接。此外,相关的是,关于完全放弃延迟请求的有趣阅读 - http://ferd.ca/rtb-where-erlang-blooms.html - cnst
我终于对这个有了更好的理解——我使用了siege命令行工具来向我的开发服务器发送一堆请求,针对一个只是睡眠10秒然后返回的端点。结果发现,有人将uwsgi的最大队列长度调整得非常大。因此,uwsgi的工作进程一直在处理非常旧的请求(一旦请求进入uwsgi队列,nginx的连接超时不会将其移除)。解决方案是:1)缩短队列长度,2)让nginx为每个请求添加一个带有时间戳的头部,并添加Django中间件来丢弃旧的请求(立即返回400错误)。 - Luke

0

看起来像是对Nginx uWSGI的DoS攻击,导致Nginx返回502、504、500状态码并使用100%的CPU。在DoS攻击中常见IP欺骗,我们可以通过检查日志来排除。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接