三十次请求开了三十条连接,关掉以后端口还要占两分钟

举报
yd_48584989 发表于 2026/11/07 11:29:11 2026/11/07
【摘要】 同一台机器上,每次都新建连接的三十个请求平均 11.64 毫秒,复用一条连接只要 1.34 毫秒,差价全在建套接字那一段;关掉的连接不会立刻消失,实测要 124 秒才从 TIME_WAIT 里退出来,按本机 16384 个动态端口算,同一目标上每秒新建连接的天花板约 136 条。

给本机跑的一个小服务压三十个请求,本来只想知道平均耗时。量完发现两个数差了将近九倍: 每次都新建连接的写法,每一次 11.64 毫秒;一条连接连发三十次的写法,每一次 1.34 毫秒。 同一台机器,同一个进程,服务端处理逻辑完全一样。

差的全在「把套接字建起来」这一段。

先把 accept 数出来

只看客户端的耗时不够,还得知道到底开了几条连接。给服务加一个计数器,覆写 get_request, 每接受一个套接字就加一:

class Srv(socketserver.ThreadingTCPServer):
    def get_request(self):
        conn, addr = super().get_request()
        accepts["n"] += 1
        return conn, addr

客户端两种写法各发三十个请求。第一种每次都 new 一个连接,请求完立刻 close; 第二种是一个连接连发三十个。量出来的数字是这样的:

写法 accept 次数 总耗时 单次均值 最慢 / 最快
每次新建连接 30 349.3 ms 11.64 ms 25.39 / 1.75 ms
一条连接发三十次 1 40.1 ms 1.34 ms 26.21 / 0.38 ms

复用那一行,最慢的 26 毫秒是第一次请求,它包含建连接;后面二十九次都在 0.4 到 2 毫秒之间。 新建那一行反过来:没有一个请求超过 26 毫秒,但最慢和最快差 14 倍。

所以短连接的「平均耗时」这个指标基本没用。它把一个固定的建连成本和一次随机的线程调度 混在一起平均,看着挺平稳,实际两个来源完全不是一回事。

关掉不等于释放

跑完立刻数一次套接字状态:

netstat -ano -p TCP
// 三十行 TIME_WAIT,本地端口是客户端的临时端口,对端端口是服务端口

三十条连接,客户端侧 30 条 TIME_WAIT,服务端侧 0 条。谁先发 FIN,这个状态就记在谁头上。

然后我盯着它们什么时候消失。起五条连接、全部关掉,每五秒数一次:

刚关完 5 条连接: TIME_WAIT=5
  t=124s -> 0

一百二十四秒。这个数来自两倍的报文最大生存时间,本机默认就是两分钟左右。 也就是说,一次请求结束之后,那个四元组还被锁着两分钟,谁都不能用。

端口池的大小是硬的

顺手把动态端口范围读出来:

netsh int ipv4 show dynamicport tcp
启动端口 : 49152
端口数   : 16384

一万六千多个端口,每条短连接占一个、占两分钟。对着同一个目标地址,能持续跑的新建连接 就是 16384 ÷ 120,约 136 条每秒。过了这个数,报错不会出现在服务端,而是本机这边 分配不到临时端口。

这个上限跟带宽、跟对方快不快都没关系,纯粹是端口池除以回收时间。 调大端口范围能挪一点,但只是在把一个除法里的分子改大,回收时间那一段动不了。

有些「复用」是假的

最后说一个把我骗过的现象。如果服务端在响应头里写 Connection: close, 客户端的连接对象并不会报错,它会丢掉旧套接字,下次请求时悄悄重建一条。 代码里只 new 了一个连接对象,服务端实际接受了三次:

第1次 status=200 累计 accept=1
第2次 status=200 累计 accept=2
第3次 status=200 累计 accept=3

返回值里一点异常都没有,状态码全是 200。我当时看着这个循环,以为它在复用, 其实每轮都付了一次建连的钱。

三条可以照抄的判断

  1. 想知道自己开了几条连接,就在服务端数 accept,或者抓包数 SYN。客户端的代码结构和对象状态都不算证据。
  2. 短连接的成本有两块:一次额外的建连,和两分钟的端口占用。平时只算前一块,会把成本低估一半。
  3. 要复用,先确认响应头里没有 Connection: close,并且准备好对端提前关闭后重发一次。没有重试的复用,等于把偶发的连接错误直接变成请求失败。

这次量完最想记住的一句:把总耗时拆开之后,那 11 毫秒里有 10 毫秒是本机在跟自己做握手, 服务端一毫秒都没花。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。