这个问题实际的原因是客户端的端口不够用了。
为什么端口会不够用呢?因为一个 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 连接。
为什么要等呢?原因主要有二:
- 最后回复的 ACK 可能丢失了,如果再收到对方发来的 FIN,还可以回复 ACK;
- 如果直接建立新的连接,那么属于当前连接的包由于乱序、延迟或者重复,可能会让对方收到,对方可能认为是属于自己的连接的包,造成问题。所以,等待 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 默认的等待时间一致,也可以判断出来是这种问题。
==计算机网络实用技术 目录==
这篇文章是计算机网络实用技术系列文章中的一篇,这个系列正在连载中,我计划用这个系列的文章来分享一些网络抓包分析的实用技术。这些文章都是总结了我的工作经历中遇到的问题,经过精心构造和编写,每个文件附带抓包文件,通过实战来学习网路分析。
如果本文对您有帮助,欢迎扫博客右侧二维码打赏支持,正是订阅者的支持,让我公开写这个系列成为可能,感谢!
没有链接的目录还没有写完,敬请期待……
- 序章
- 抓包技术以及技巧
- 理解网络的分层模型
- 数据是如何路由的
- 网络问题排查的思路和技巧
- 不可以用路由器?
- 网工闯了什么祸?
- 网络中的环路和防环技术
- 延迟增加了多少?
- TCP 延迟分析
- 压测的时候 QPS 为什么上不去?
- 压测的时候 QPS 为什么上不去?答案和解析
- 重新认识 TCP 的握手和挥手
- 重新认识 TCP 的握手和挥手:答案和解析
- TCP 下载速度为什么这么慢?
- TCP 长肥管道性能分析
- 后记:学习网络的一点经验分享
与本博客的其他页面不同,本页面使用 署名-非商业性使用-禁止演绎 4.0 国际 协议。