压测的时候 QPS 为什么上不去?答案和解析

这个问题实际的原因是客户端的端口不够用了。

为什么端口会不够用呢?因为一个 TCP 连接的标志是四元组:

(src ip, src port, dst ip, dst port)

在这个场景中,代理服务器去连接 Real Server:

  • 代理服务器的 src ip 确定
  • 代理服务器的 src port 是随机指定
  • dst ip 是 Real Server 的 ip
  • dst port 是 Real Server 的 port

所以,能让 TCP 四元组不一样的字段,就只有 src port 了。

那么 Linux 服务器在连接远程服务器的 80 端口的时候,本地端口会用什么呢?答案是随机指定的。但是我们可以设置随机指定的范围。通过 sysctl -w net.ipv4.ip_local_port_range="32768 65535" 命令,可以让 client port 使用 32768 到 65535 之间的值。这样,低于 32768 的端口可以让其他服务 listen。

Local Port 不够用的一些场景

默认的端口就有 3 万个可用,所以大部分的情况下是很够用的。况且,这是在 client ip, dst ip, dst port 都确定的情况下最多可以建 3 万个连接。如果 dst ip 和 dst port 不固定,比如同一个 HTTP 服务在同一个 Server IP listen 了两个端口,那么就是最多 6 万个连接。如果部署多个实例,不同的 IP,那么每一个 IP 都可以是 3 万个连接。这么大的连接数量,一般来说代码性能甚至硬件(网卡)性能会首先到达瓶颈。

什么情况下会遇到端口不够用呢?

一种就是如上所说,一个代理程序去直连另一个真实服务器,两边的 IP 固定了,一边的端口固定了,那么 client 侧端口最多 3 万的话,在 QPS 大的情况下可能会遇到端口不够用的情况。

理论上最多可以有 3 万个并发,为什么在实际的情况中达不到这么高的并发呢?因为在一个 TCP 连接结束之后,这个 client port 并不是马上可以用来创建一个新的 TCP 连接。在 TCP 的状态机中,主动关闭 TCP 连接的一方会进入 TIME_WAIT 状态。需要在这个状态等待 2MSL (Maximum Segment Lifetime,最大报文生存时间,在 Linux 中,默认是 1 分钟的等待时间),然后这个 TCP 连接才会完全释放,client 端口才可以被重新用来建立新的 TCP 连接。

为什么要等呢?原因主要有二:

  1. 最后回复的 ACK 可能丢失了,如果再收到对方发来的 FIN,还可以回复 ACK;
  2. 如果直接建立新的连接,那么属于当前连接的包由于乱序、延迟或者重复,可能会让对方收到,对方可能认为是属于自己的连接的包,造成问题。所以,等待 2MSL 可以确保连接相关的数据包在网络中完全消失;
图来自维基百科

那么这种情况该如何解决呢?

首先可以调整参数,sysctl -w net.ipv4.ip_local_port_range="10000 65535" 就可以有更多的可用端口。

另一种就是用长连接,不那么频繁地建立连接,也就没有反复创建连接的端口问题了。

TIME_WAIT 状态的行为是可以通过参数调整的,通过 sysctl -w net.ipv4.tcp_tw_reuse=1 设置,可以让处于 TIME_WAIT 状态的端口用于创建新的 TCP 连接。(但是可能带来其他问题)

还有一种情况会遇到 local port 不够用,就是 NAT 设备,source IP 可能有很多,但是经过了 NAT,NAT 上的 TCP 连接就都是 NAT 的 IP 了,很容易造成四元组不够用。NAT 上面的问题最好的办法是增加出口 IP。

抓包如何分析?

到这里,首先向读者致歉,在写分析的时候,我发现这个例子其实并不好完全通过抓包来分析解决。因为出问题的时候,客户端角度的包并没有发出来,抓包也就抓不到这个包。所以这个例子选的不合适。

这个例子最好的排查方法是通过客户端侧的网络状态来排查。直接通过 ss -s 命令,可以直接看到处于 timewait 状态的连接。

ss -s 命令查看连接状态

如果很高(占用了可用 local 端口范围的大部分),就说明瓶颈在这里了。

通过 tcp.flags.syn==1 and tcp.dstport == 80 and tcp.srcport == 65531 这个条件来过滤,我们可以查看同一个 local port 建立连接的历史。

连接建立的历史

打开 Delta Time,可以看到这个端口每次复用的时间在 60s 之后了,和 Linux timewait 默认的等待时间一致,也可以判断出来是这种问题。

==计算机网络实用技术 目录==

这篇文章是计算机网络实用技术系列文章中的一篇,这个系列正在连载中,我计划用这个系列的文章来分享一些网络抓包分析的实用技术。这些文章都是总结了我的工作经历中遇到的问题,经过精心构造和编写,每个文件附带抓包文件,通过实战来学习网路分析。

如果本文对您有帮助,欢迎扫博客右侧二维码打赏支持,正是订阅者的支持,让我公开写这个系列成为可能,感谢!

没有链接的目录还没有写完,敬请期待……

  1. 序章
  2. 抓包技术以及技巧
  3. 理解网络的分层模型
  4. 数据是如何路由的
  5. 网络问题排查的思路和技巧
  6. 不可以用路由器?
  7. 网工闯了什么祸?
  8. 网络中的环路和防环技术
  9. 延迟增加了多少?
  10. TCP 延迟分析
  11. 压测的时候 QPS 为什么上不去?
  12. 压测的时候 QPS 为什么上不去?答案和解析
  13. 重新认识 TCP 的握手和挥手
  14. 重新认识 TCP 的握手和挥手:答案和解析
  15. TCP 下载速度为什么这么慢?
  16. TCP 长肥管道性能分析
  17. 后记:学习网络的一点经验分享
与本博客的其他页面不同,本页面使用 署名-非商业性使用-禁止演绎 4.0 国际 协议。


Leave a comment

您的邮箱地址不会被公开。 必填项已用 * 标注