nginx 是怎么实现 tps>2000 的, 理论上这不可能呀 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
bthulu
V2EX    程序员

nginx 是怎么实现 tps>2000 的, 理论上这不可能呀

  •  
  •   bthulu 2024-05-27 18:08:21 +08:00 3317 次点击
    这是一个创建于 502 天前的主题,其中的信息可能已经有所发展或是发生改变。

    短链接有 time_wait 时间, 最少 30 秒.

    可用端口最多 65535.

    那理论上短链接最大 TPS 只有 65535/30=2184, nginx 是用了啥魔法突破了 TCP 协议限制吗?

    25 条回复    2024-05-28 17:40:49 +08:00
    codehz
        1
    codehz  
       2024-05-27 18:44:25 +08:00
    首先这是服务端,端口是固定的,除非你把 benchmark 也跑在同一个网卡上(如 localhost )
    其次可以 http keep-alive
    再三 benchmark 程序可以使用端口复用
    phrack
        2
    phrack  
       2024-05-27 18:46:07 +08:00 via iPhone
    什么菜鸡
    InkStone
        3
    InkStone  
       2024-05-27 18:48:38 +08:00
    TCP/UDP 连接是否重复是四元组(dstPort,dstIp, srcPort,srcIp)决定的,而不是你理解的单个 port
    ipwx
        4
    ipwx  
       2024-05-27 18:54:34 +08:00
    同一个 (server, serverPort) 可以被很多不同的 (client, clientPort) 同时连接。
    deplives
        5
    deplives  
       2024-05-27 19:02:42 +08:00
    你这计网全还给老师了还是根本没学
    flyqie
        6
    flyqie  
       2024-05-27 19:15:05 +08:00
    四元组。。。

    你这知识都忘了?
    bthulu
        7
    bthulu  
    OP
       2024-05-27 19:23:01 +08:00
    @InkStone 这跟四元组有什么关系? nginx 压测, 后端起一个服务器, nginx 代理请求到这个服务器, 测试端全部是短链接请求, dstPort,dstIp, srcIp 都是固定的, 只有 srcPort 是变化的.
    mxT52CRuqR6o5
        8
    mxT52CRuqR6o5  
       2024-05-27 19:25:24 +08:00
    nginx 不止有反向代理功能,也能直接 serve 本地静态文件,不作反向代理服务器时 nginx 就不需要作为客户端去访问某个服务器了
    chenyu0x00
        9
    chenyu0x00  
       2024-05-27 19:26:44 +08:00
    @bthulu 比如 nginx 监听 443 端口,IP 是 1.1.1.1 ,一个 client 的 IP 是 2.2.2.2 ,那么这个 client 理论上可以向 nginx 建立 65535 个连接,每个连接的四元组在 client 上看是(src ip = 2.2.2.2, src port=1 到 65535 ,dst ip=1.1.1.1, dst port = 443),在 nginx 上看的话 src 和 dst 会交换。但是如果有另一个 client 的 IP 是 3.3.3.3 ,那么又可以和 nginx 建立 65535 个连接,这样 nginx 就可以同时服务 65535*2 个连接,这些连接的四元组是不同的,所以不会出问题。
    bthulu
        10
    bthulu  
    OP
       2024-05-27 19:28:36 +08:00
    @codehz nginx 不是服务端呀, nginx 是做反向代理, 是客户端. benchmark 程序可以使用端口复用, nginx 应该是不会端口复用的吧?
    也就是说, 就是 keep-alive 确保了可以突破 2000?
    bthulu
        11
    bthulu  
    OP
       2024-05-27 19:30:54 +08:00
    @chenyu0x00 但是 nginx 反代呢, 他跟后端服务器之间, nginx 的 IP 是固定的, 后端服务器的 IP 和端口也是固定的, 这个时候只有 nginx 的端口是动态的了. 可不可以认为这种情况下, nginx 的 TPC 不可能>2000?
    pengjay
        12
    pengjay  
       2024-05-27 19:31:08 +08:00
    一台客户端对一台服务器理论是这样的。端口用完没释放的话就建立不了链接了
    chenyu0x00
        13
    chenyu0x00  
       2024-05-27 19:32:15 +08:00
    @bthulu 另外同一个 TCP 连接在完成一个请求之后可以不断开继续完成下一个请求(也叫长连接),如果单个请求很简单(比如发送 index.html)的话,是可以在短时间内完成多次请求的。一般 http 压力测试的话都会启用长连接,因为长连接更考验 nginx 的性能,如果每次都新建 TCP 连接的话考验的是操作系统和 TCP 协议的性能
    chenyu0x00
        14
    chenyu0x00  
       2024-05-27 19:32:56 +08:00
    @bthulu #11 nginx 反向代理也会启用长连接
    bthulu
        15
    bthulu  
    OP
       2024-05-27 19:39:10 +08:00
    @chenyu0x00 我就说为啥我这突破不了 2000 了, 我还以为哪里配置有问题. 我这里是代理了 Modbus 主服务器, 走的是短链接 tcp 协议, 不是 http. Modbus 主服务器是施耐德的 PLC, 限制只能短链接. 这种情况下, 还有什么别的办法突破 2000 这个限制吗?
    ccnoobs
        16
    ccnoobs  
       2024-05-27 19:41:40 +08:00
    同一台 client ( ip 固定 多线程跑) 和同一台 service 在同一时刻(一瞬间)下确实不会超过这个限制
    不过具体请求时无法保证同一时刻
    chenyu0x00
        17
    chenyu0x00  
       2024-05-27 19:42:11 +08:00
    @bthulu #15 纯 tcp 协议我接触得不多,你可以试试在 server 上多监听几个端口,或者问问 ChatGPT 看能不能调整一些系统参数
    bthulu
        18
    bthulu  
    OP
       2024-05-27 21:11:47 +08:00
    @InkStone
    @flyqie
    @chenyu0x00
    @pengjay
    @ccnoobs
    刚才实测, 同一个客户端, 先向服务端 1 发送 7 万次请求, 再向服务端 2 发送 7 万次请求.

    结果是向服务端 1 发送到 6 万多次时就报 SocketException: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

    再向服务端 2 发请求时, 一次请求都发不出去.

    实践证明在 windows 上, 端口是共用的, 若通过端口 9000 请求 dstIp1 并关闭后, 9000 端口进入 time_wait 状态, 此时往其他 dstIp 发送请求时也无法使用这个端口.

    服务端代码
    ```C#
    using System.Net;
    using System.Net.Sockets;

    int port = 9000;
    TcpListener server = new TcpListener(IPAddress.Any, port);
    server.Start();
    Console.WriteLine($"Server listening on :{port}");

    try
    {
    var writer = File.AppendText("c:/temp/a.txt");
    List<int> ports = [];
    while (true)
    {
    TcpClient client = server.AcceptTcpClient();
    IPEndPoint endPoint = (IPEndPoint)client.Client.RemoteEndPoint!;
    ports.Add(endPoint.Port);
    if (ports.Count > 10)
    {
    writer.WriteLine(string.Join("\r\n", ports));
    writer.Flush();
    ports.Clear();
    }
    }
    }
    catch (SocketException e)
    {
    Console.WriteLine($"SocketException: {e}");
    }
    finally
    {
    server.Stop();
    }
    ```

    客户端代码
    ```C#
    public class UnitTest1
    {
    [TestMethod]
    public async Task TestMethod1()
    {
    int k = 0;
    for (int j = 0; j < 70; j++)
    {
    List<Task> list = [];
    for (int i = 0; i < 1000; i++)
    {
    k++;
    Task task = OpenAndCloseTcp("10.98.20.129");
    list.Add(task);
    }

    await Task.WhenAll(list);
    Console.WriteLine(k);
    }
    }

    [TestMethod]
    public async Task TestMethod2()
    {
    int k = 0;
    for (int j = 0; j < 70; j++)
    {
    List<Task> list = [];
    for (int i = 0; i < 1000; i++)
    {
    k++;
    Task task = OpenAndCloseTcp("10.98.20.130");
    list.Add(task);
    }

    await Task.WhenAll(list);
    Console.WriteLine(k);
    }
    }

    private async Task OpenAndCloseTcp(string ip)
    {
    using var client = new TcpClient();
    await client.ConnectAsync(ip, 9000);
    await using NetworkStream stream = client.GetStream();
    byte[] bytes = "\r\n"u8.ToArray();
    await stream.WriteAsync(bytes);
    }
    }
    ```
    dhb233
        19
    dhb233  
       2024-05-28 09:37:00 +08:00
    nginx 到后端不能连接复用?有大量连接的情况,只能扩充 IP 地址了,4 层 LB 是这么干的
    zhuisui
        20
    zhuisui  
       2024-05-28 10:22:09 +08:00
    上面的人都没理解你的问题
    你的问题关键是连接进入了 time_wait 状态,此时连接还没有完全关闭,妨碍了新的连接建立。
    正常关闭的连接不会进入 time_wait 状态,而是直接 closed 然后消失。
    另外你也可以缩短连接在 time_wait 状态停留的时间,Windows 上怎么弄我不知道
    seedhk
        21
    seedhk  
       2024-05-28 11:29:28 +08:00   1
    班门弄斧一下,有问题请指正:

    "可用端口最多 65535."

    1. 就像 1 楼 说的:
    站在服务器接收数据的角度,一般都是指定某个端口,比如 80 端口用于接收请求。

    可用端口数是站在客户端的角度来说的,因为客户端发起一个请求,除非绑定了端口,否则都是随机选择一个高位端口。


    "那理论上短链接最大 TPS 只有 65535/30=2184"

    2.TCP 通过四元组确定一个链接是否重用

    源 IP 地址 (srcIp)
    源端口号 (srcPort)
    目的 IP 地址 (dstIp)
    目的端口号 (dstPort)
    对于服务器来说,dstIp 和 dstPort 是固定的,而 srcIp 和 srcPort 是可变的。

    所以,在 IPV4 中,绝对理想的情况下(不考虑操作系统,内存大小,请求耗时,请求大小,time_wait 等)
    一个服务端能接受的最大请求数应该是:

    客户端 ip 数×客户端 port 数


    #10
    "nginx 不是服务端呀, nginx 是做反向代理, 是客户端"

    nginx 作为反向代理接收数据时,这时候他就已经是“服务端”了,绑定了某个端口(比如 80)
    seedhk
        22
    seedhk  
       2024-05-28 11:42:14 +08:00
    接 #21

    如果 你是按照 #18 的写法的话,那么讨论的问题其实是,客户端最多能同时向外发送多少个请求,从这个角度来说,你说的确实没错。

    但是这和 nginx 有啥关系呢
    ysc3839
        23
    ysc3839  
       2024-05-28 15:08:28 +08:00 via Android
    反向代理换成 Unix socket 就解决问题了
    ysc3839
        24
    ysc3839  
       2024-05-28 15:09:52 +08:00 via Android
    @ysc3839 实在不能换,127.0.0.0/8 整个网段都能作为回环地址使用,换着用也能解决问题。
    paceewang1
        25
    paceewang1  
       2024-05-28 17:40:49 +08:00
    你这个提问的方式有点问题,大部分兄弟都是没仔细看你的问题的,nginx 确实在某些反代的场景需要和后端建立 1 到 1 的多个单独长连接,比如说 ws ,tcp 等,这种情况下确实会出现端口耗尽的情况,解决的话可以在 nginx 上绑定虚拟 ip+端口。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     978 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 23:04 PVG 07:04 LAX 16:04 JFK 19:04
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86