你所不知道的端口耗尽(一)

有同事联系我说,在生产环境上,访问不了我负责的common服务,然后我去检查common服务的health endpoint, 没问题,然后我问了下异常,timeout导致的System.OperationCanceledException。那大概率是客户端的问题,会不会是端口耗尽,用netstat看下是不是有大量的端口占用

果然如此,大概如图:

time-wait-state-entries

什么是端口耗尽

端口耗尽(Port Exhaustion)是指当系统中可用的TCP/IP端口号被全部占用,而无法建立新的网络连接时产生的一种情况。例如,在进行大量的网络连接,如客户端与服务器之间频繁地建立和断开连接时,可能会发生端口耗尽。如果客户端在短时间内打开了大量的短暂连接,并且由于TCP协议的TIME_WAIT状态,这些端口不能立即被重用,就可能出现端口耗尽的现象。此时,新的出站连接可能会失败,因为找不到可用的源端口号。

端口是有限的

每个TCP或UDP连接由一个四元组唯一标识:源IP地址、源端口号、目的IP地址以及目的端口号。在一个给定的源IP地址中,端口号是有限的,通常是从1024到65535(0到1023保留给系统和知名端口)。

拓展阅读,如何查看Linux中的可用端口范围

1
cat /proc/sys/net/ipv4/ip_local_port_range

什么是TIME_WAIT状态

在TCP/IP网络中,TIME_WAIT状态是TCP连接结束过程的一部分。这是一个正常的状态,发生在一个连接关闭的准备阶段,此时通常是客户端已经发送了一个FIN(结束)数据包来表示它没有更多数据要发送,并且也已经收到了另一侧(通常是服务器)的确认。

下面是TIME_WAIT状态在TCP连接终止过程中的作用:

  • 主动关闭:客户端向服务器发送一个FIN数据包,表示它已经完成数据发送。
  • 服务器接收到FIN,发送一个ACK(确认),并进入CLOSE_WAIT状态,表示它已经确认了客户端结束连接的请求。
  • 服务器在发送完所有剩余的数据后,发送自己的FIN数据包。
  • 客户端接收到服务器的FIN,发送回一个ACK,并进入TIME_WAIT状态。

tcp-termination

TIME_WAIT状态会持续一个时间段,这个时间是最大报文生命周期(MSL)的两倍,这是一个确保连接相关的所有数据包不再存在于网络中的定义期间。这个时间通常被设置为2分钟,__因此TIME_WAIT状态的持续时间通常是4分钟__。

TIME_WAIT状态的主要原因有:

  • 确保网络上延迟的数据包被丢弃,避免可能干扰后续新的连接。
  • 允许TCP连接可靠地结束,确保FIN-ACK握手过程正确结束。
  • 确保如果客户端到服务器的最后一个ACK丢失,客户端仍处于TIME_WAIT状态,这样如果服务器因为没有收到ACK而重新发送FIN,客户端可以重新发送ACK。

在高流量的服务器中,TCP连接经常地开启和关闭,TIME_WAIT状态以及它的持续时间可能导致大量的套接字处于这个状态,从而可能耗尽可用的端口。

如何解决

  • 改变TIME_WAIT时间,即减少端口被占用的时间;不推荐,尽量使用默认设置
  • 启用端口重用,例如TCP端口复用(SO_REUSEADDR)选项;
  • 增加可用的端口范围。
  • 保证端口尽早尽快的释放