为什么当监听队列已满时uWSGI不拒绝请求?

Tob*_*ann 3 python queue webserver request uwsgi

给出以下最小示例:

# myproject.py

import time

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello():
    time.sleep(5)
    return 'hello'


if __name__ == '__main__':
    app.run(host='0.0.0.0')
Run Code Online (Sandbox Code Playgroud)
# wsgi.py

from myproject import app

if __name__ == '__main__':
    app.run()
Run Code Online (Sandbox Code Playgroud)
uwsgi --http-socket 0.0.0.0:8080 --workers 1 --listen 2 --module wsgi:app
Run Code Online (Sandbox Code Playgroud)

我现在预计,同时发送 3 个以上的请求(1 个在工作线程中正在进行,2 个已排队)将导致只有 3 个请求得到服务,而其他请求则被拒绝。

然而,情况似乎并非如此。当像这样发送 10 个请求时

curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080 & \
curl http://127.0.0.1:8080
Run Code Online (Sandbox Code Playgroud)

全部都一一成功送达。为什么会这样呢?我是否误解/错误配置了什么?

(我使用的是 Ubuntu 20.04,以防万一这很重要。)

小智 6

我不确定,低级网络不是我的专业领域,但我相信我已经找到了答案

我发现几年前的一个问题与你的非常相似。有人发现 uwsgi 排队的响应数量超出了指定listen值应允许的数量。 https://uwsgi.unbit.narkive.com/QKdRyejv/when-the-backlog-is-full-is-uwsgi-supposed-to-accept-connections

在页面底部附近我们可以看到:

是的,这是 Linux 的预期行为,监听队列的最小大小始终强制为 8(在 BSD 中为 5)。

为了确认这实际上是正确的,我做了更多的挖掘,发现监听值实际上只是等待多少个的提示,并且实现可能会监听不同的数量。

https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html

backlog 参数为实现提供了一个提示,实现应使用该提示来限制套接字侦听队列中未完成连接的数量。实现可能会对积压施加限制并默默地减少指定值。通常,较大的积压参数值将导致侦听队列的长度较大或相等。实现应支持高达 SOMAXCONN 的积压值,在 <sys/socket.h> 中定义。

Listen() 忽略积压参数?引导我检查实际的 Linux 内核源代码以确认最初的说法。

http://lxr.linux.no/#linux+v2.6.36/net/core/request_sock.c#L44我们看到似乎是一个确认

43        nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);
44        nr_table_entries = max_t(u32, nr_table_entries, 8);
45        nr_table_entries = roundup_pow_of_two(nr_table_entries + 1);
Run Code Online (Sandbox Code Playgroud)

这似乎首先使用nr_table_entriesor sysctl_max_syn_backlog(常数 256),以较小者为准。在我们的示例中nr_table_entries应该是 2。

接下来,它选择其中的最大值和 8,因此我们的 2 被丢弃并使用 8。

然后四舍五入到下一个最高的 2 次方。

我用更多流量(100 个并发请求)淹没了您的示例服务器,但除了 9 个之外的所有请求都失败了。我相当确信这可以解释您所看到的行为。实际上你的聆听价值不可能那么低。