如何解决nginx阻塞请求直到当前请求完成
将我的问题归结为最简单的可能:我有一个简单的 Flask 网络服务器,它有一个像这样的 GET 处理程序:
@app.route('/',methods=['GET'])
def get_handler():
t = os.environ.get("SLOW_APP")
app_type = "Fast"
if t == "1":
app_type = "Slow"
time.sleep(20)
return "Hello from Flask,app type = %s" % app_type
我在两个不同的端口上运行这个应用程序:一个没有在端口 8000 上设置 SLOW_APP 环境变量,另一个在端口 8001 上设置了 SLOW_APP 环境变量。 接下来,我有一个 nginx 反向代理,它的上游有这两个 appserver 实例。我正在使用 docker 运行所有内容,因此我的 nginx conf 如下所示:
upstream myproject {
server host.docker.internal:8000;
server host.docker.internal:8001;
}
server {
listen 8888;
#server_name www.domain.com;
location / {
proxy_pass http://myproject;
}
}
它的工作原理是,如果我打开两个浏览器窗口并输入 localhost,它首先访问慢速服务器,需要 20 秒,在此期间第二个浏览器似乎阻止等待第一个请求完成。最终我看到第一个请求由“慢”服务器提供服务,第二个请求由“快速”服务器提供服务(没有 time.sleep())。为什么 nginx 会阻塞第二个请求,直到第一个请求完成?
解决方法
不,如果第一个请求发送到慢速服务器(需要 20 秒),并且在此延迟期间,如果我再次从浏览器发出请求,它会发送到第二个服务器,但仅在第一个服务器完成之后。
>我曾与我们的工程团队就此进行过合作,可以分享以下见解:
我们的实验室环境
Lua
load_module modules/ngx_http_lua_module-debug.so;
...
upstream app {
server 127.0.0.1:1234;
server 127.0.0.1:2345;
}
server {
listen 1234;
location / {
content_by_lua_block {
ngx.log(ngx.WARN,"accepted by fast")
ngx.say("accepted by fast")
}
}
}
server {
listen 2345;
location / {
content_by_lua_block {
ngx.log(ngx.WARN,"accepted by slow")
ngx.say("accepted by slow")
ngx.sleep(5);
}
}
}
server {
listen 80;
location / {
proxy_pass http://app;
}
}
这与我们将流量代理到的另一个 3rd 方应用程序的设置相同。但是我已经使用您的问题中共享的 NGINX 配置和两个基于 NodeJS 的应用程序作为上游进行了测试。
NodeJS
正常
const express = require('express');
const app = express();
const port = 3001;
app.get ('/',(req,res) => {
res.send('Hello World')
});
app.listen(port,() => {
console.log(`Example app listening on ${port}`)
})
慢
const express = require('express');
const app = express();
const port = 3002;
app.get ('/',res) => {
setTimeout( () => {
res.send('Hello World')
},5000);
});
app.listen(port,() => {
console.log(`Example app listening on ${port}`)
})
测试
当我们使用 NGINX OSS 时,负载平衡协议将是 RoundRobin (RR)。我们使用 ap
从另一台服务器进行的第一次测试。结果:
Concurrency Level: 10
Time taken for tests: 25.056 seconds
Complete requests: 100
Failed requests: 0
Total transferred: 17400 bytes
HTML transferred: 1700 bytes
Requests per second: 3.99 [#/sec] (mean)
Time per request: 2505.585 [ms] (mean)
Time per request: 250.559 [ms] (mean,across all concurrent requests)
Transfer rate: 0.68 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.7 0 5
Processing: 0 2505 2514.3 5001 5012
Waiting: 0 2504 2514.3 5001 5012
Total: 1 2505 2514.3 5001 5012
Percentage of the requests served within a certain time (ms)
50% 5001
66% 5005
75% 5007
80% 5007
90% 5010
95% 5011
98% 5012
99% 5012
100% 5012 (longest request)
50% 的请求都很慢。那完全没问题,因为我们有一个“慢”实例。与 curl
相同的测试。结果一样。根据 NGINX 服务器的调试日志,我们看到请求在进入时被处理并被发送到慢速或快速后端(基于循环)。
2021/04/08 15:26:18 [debug] 8995#8995: *1 get rr peer,try: 2
2021/04/08 15:26:18 [debug] 8995#8995: *1 get rr peer,current: 000055B815BD4388 -100
2021/04/08 15:26:18 [debug] 8995#8995: *4 get rr peer,try: 2
2021/04/08 15:26:18 [debug] 8995#8995: *4 get rr peer,current: 000055B815BD4540 0
2021/04/08 15:26:18 [debug] 8995#8995: *5 get rr peer,try: 2
2021/04/08 15:26:18 [debug] 8995#8995: *5 get rr peer,current: 000055B815BD4388 -100
2021/04/08 15:26:18 [debug] 8995#8995: *7 get rr peer,try: 2
2021/04/08 15:26:18 [debug] 8995#8995: *7 get rr peer,current: 000055B815BD4540 0
2021/04/08 15:26:18 [debug] 8995#8995: *10 get rr peer,try: 2
2021/04/08 15:26:18 [debug] 8995#8995: *10 get rr peer,current: 000055B815BD4388 -100
2021/04/08 15:26:18 [debug] 8995#8995: *13 get rr peer,try: 2
2021/04/08 15:26:18 [debug] 8995#8995: *13 get rr peer,current: 000055B815BD4540 0
2021/04/08 15:26:18 [debug] 8995#8995: *16 get rr peer,try: 2
2021/04/08 15:26:18 [debug] 8995#8995: *16 get rr peer,current: 000055B815BD4388 -100
2021/04/08 15:26:18 [debug] 8995#8995: *19 get rr peer,try: 2
2021/04/08 15:26:18 [debug] 8995#8995: *19 get rr peer,current: 000055B815BD4540 0
因此,这意味着“nginx 阻塞请求直到当前请求完成”的行为在实例上无法重现。但是我能够在 Chrome 浏览器中重现您的问题。命中慢速实例将让另一个浏览器窗口等待第一个得到响应。在客户端进行了一些内存分析和调试后,我遇到了浏览器的连接池。
https://www.chromium.org/developers/design-documents/network-stack
浏览器使用与服务器相同的、已经建立的连接。如果此连接被等待请求(相同数据,相同 cookie...)占用,它将不会从池中打开新连接。它将等待第一个请求完成。您可以通过向请求添加缓存破坏者或新标头、新 cookie 来解决此问题。类似:
http://10.172.1.120:8080/?ofdfu9aisdhffadf
。当您在另一个浏览器窗口中等待响应时,将其发送到一个新的浏览器窗口中。这将显示立即响应(假设没有其他对后端的请求,因为基于 RR -> 如果有对慢速的请求,则下一个将是快速的)。
如果您从不同的客户端发送请求,同样适用。这也可以。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。