短链接有 time_wait 时间, 最少 30 秒.
可用端口最多 65535.
那理论上短链接最大 TPS 只有 65535/30=2184, nginx 是用了啥魔法突破了 TCP 协议限制吗?
1
codehz 214 天前
首先这是服务端,端口是固定的,除非你把 benchmark 也跑在同一个网卡上(如 localhost )
其次可以 http keep-alive 再三 benchmark 程序可以使用端口复用 |
2
phrack 214 天前 via iPhone
什么菜鸡
|
3
InkStone 214 天前
TCP/UDP 连接是否重复是四元组(dstPort,dstIp, srcPort,srcIp)决定的,而不是你理解的单个 port
|
4
ipwx 214 天前
同一个 (server, serverPort) 可以被很多不同的 (client, clientPort) 同时连接。
|
5
deplives 214 天前
你这计网全还给老师了还是根本没学
|
6
flyqie 214 天前
四元组。。。
你这知识都忘了? |
7
bthulu OP @InkStone 这跟四元组有什么关系? nginx 压测, 后端起一个服务器, nginx 代理请求到这个服务器, 测试端全部是短链接请求, dstPort,dstIp, srcIp 都是固定的, 只有 srcPort 是变化的.
|
8
mxT52CRuqR6o5 214 天前
nginx 不止有反向代理功能,也能直接 serve 本地静态文件,不作反向代理服务器时 nginx 就不需要作为客户端去访问某个服务器了
|
9
chenyu0x00 214 天前
@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 个连接,这些连接的四元组是不同的,所以不会出问题。
|
10
bthulu OP @codehz nginx 不是服务端呀, nginx 是做反向代理, 是客户端. benchmark 程序可以使用端口复用, nginx 应该是不会端口复用的吧?
也就是说, 就是 keep-alive 确保了可以突破 2000? |
11
bthulu OP @chenyu0x00 但是 nginx 反代呢, 他跟后端服务器之间, nginx 的 IP 是固定的, 后端服务器的 IP 和端口也是固定的, 这个时候只有 nginx 的端口是动态的了. 可不可以认为这种情况下, nginx 的 TPC 不可能>2000?
|
12
pengjay 214 天前
一台客户端对一台服务器理论是这样的。端口用完没释放的话就建立不了链接了
|
13
chenyu0x00 214 天前
@bthulu 另外同一个 TCP 连接在完成一个请求之后可以不断开继续完成下一个请求(也叫长连接),如果单个请求很简单(比如发送 index.html)的话,是可以在短时间内完成多次请求的。一般 http 压力测试的话都会启用长连接,因为长连接更考验 nginx 的性能,如果每次都新建 TCP 连接的话考验的是操作系统和 TCP 协议的性能
|
14
chenyu0x00 214 天前
@bthulu #11 nginx 反向代理也会启用长连接
|
15
bthulu OP @chenyu0x00 我就说为啥我这突破不了 2000 了, 我还以为哪里配置有问题. 我这里是代理了 Modbus 主服务器, 走的是短链接 tcp 协议, 不是 http. Modbus 主服务器是施耐德的 PLC, 限制只能短链接. 这种情况下, 还有什么别的办法突破 2000 这个限制吗?
|
16
ccnoobs 214 天前
同一台 client ( ip 固定 多线程跑) 和同一台 service 在同一时刻(一瞬间)下确实不会超过这个限制
不过具体请求时无法保证同一时刻 |
17
chenyu0x00 214 天前
@bthulu #15 纯 tcp 协议我接触得不多,你可以试试在 server 上多监听几个端口,或者问问 ChatGPT 看能不能调整一些系统参数
|
18
bthulu OP @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); } } ``` |
19
dhb233 213 天前
nginx 到后端不能连接复用?有大量连接的情况,只能扩充 IP 地址了,4 层 LB 是这么干的
|
20
zhuisui 213 天前
上面的人都没理解你的问题
你的问题关键是连接进入了 time_wait 状态,此时连接还没有完全关闭,妨碍了新的连接建立。 正常关闭的连接不会进入 time_wait 状态,而是直接 closed 然后消失。 另外你也可以缩短连接在 time_wait 状态停留的时间,Windows 上怎么弄我不知道 |
21
seedhk 213 天前 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) |
22
seedhk 213 天前
接 #21
如果 你是按照 #18 的写法的话,那么讨论的问题其实是,客户端最多能同时向外发送多少个请求,从这个角度来说,你说的确实没错。 但是这和 nginx 有啥关系呢 |
23
ysc3839 213 天前 via Android
反向代理换成 Unix socket 就解决问题了
|
25
paceewang1 213 天前
你这个提问的方式有点问题,大部分兄弟都是没仔细看你的问题的😂,nginx 确实在某些反代的场景需要和后端建立 1 到 1 的多个单独长连接,比如说 ws ,tcp 等,这种情况下确实会出现端口耗尽的情况,解决的话可以在 nginx 上绑定虚拟 ip+端口。
|