太多的TIME_WAIT连接,出现“无法分配请求的地址”错误。

4

我有一个小型的Web应用程序,它打开一个TCP套接字连接,发出一个命令,读取响应,然后关闭每个请求到特定REST端点的连接。

我已经开始使用Apache JMeter对端点进行负载测试,并注意到在运行一段时间后,我开始看到像“无法分配请求的地址”这样的错误,打开此连接的代码如下:

def lookup(word: String): Option[String] = {
 try {
  val socket = new Socket(InetAddress.getByName("localhost"), 2222)
  val out = new PrintStream(socket.getOutputStream)
  val reader = new BufferedReader(new InputStreamReader(socket.getInputStream, "utf8"))
  out.println("lookup " + word)
  out.flush()

  var curr = reader.readLine()
  var response = ""
  while (!curr.contains("SUCC") && !curr.contains("FAIL")) {
    response += curr + "\n"
    curr = reader.readLine()
  }
  socket.close()
  curr match {
    case code if code.contains(SUCCESS_CODE) => {
      Some(response)
    }
    case _ => None
  }
 }
 catch {
   case e: Exception => println("Got an exception "+ e.getMessage); None
 }
}

当我运行netstat时,我也看到了很多TIME_WAIT连接状态,这意味着我在临时端口空间中的端口已经用完。

tcp6       0      0 localhost:54646         localhost:2222          TIME_WAIT  
tcp6       0      0 localhost:54638         localhost:2222          TIME_WAIT  
tcp6       0      0 localhost:54790         localhost:2222          TIME_WAIT  
tcp6       0      0 localhost:54882         localhost:2222          TIME_WAIT 

我想知道解决这个问题的最佳方案是什么。我目前的想法是创建一个连接池,使运行在端口 2222 上的该服务的连接可以被不同的HTTP请求重复使用,而不是每次都创建新的请求。这种方式修复问题、使应用程序扩展更好可行吗?似乎引入了很多开销并且使我的应用程序更加复杂。
除了以上的方法外,是否还有其他帮助应用程序扩展并克服这个端口问题的解决方案?我的Web应用程序运行在Ubuntu Linux虚拟机上。

1
您需要使用SO_REUSE_ADDR吗?在这里查看答案:https://dev59.com/rWYq5IYBdhLWcg3wjBQM - The Archetypal Paul
1
还有http://stackoverflow.com/questions/16014627/tcp-connection-cant-be-established-when-there-is-a-tcp-connection-with-state-t?rq=1和其他几百万个相关问题 - 请参见右侧的列表-> - The Archetypal Paul
3个回答

6
是的,创建连接池是一个好的解决方案。但更简单的解决方案是让服务器关闭连接,而不是客户端。在这种情况下,服务器的套接字而不是客户端的套接字会进入TIME_WAIT状态,因此客户端不会用尽端口。在服务器端,处于TIME_WAIT状态的连接不会使服务器用尽端口,因为它们都使用相同的本地端口。
为了确保连接由服务器关闭,您需要从套接字(在客户端上)读取,直到达到文件结束条件。此时,可以安全地关闭客户端侧的套接字,因为服务器已经关闭了它。当然,您需要确保服务器将关闭套接字,而不是等待新请求。
另外,如果您有root访问权限,则可以调整一些sysctl选项。
  • net.ipv4.ip_local_port_range – 临时端口的范围。增加它可以为出站连接提供更多的端口。
  • net.ipv4.tcp_tw_recycle – 启用更快地回收处于TIME_WAIT状态的连接。
  • net.ipv4.tcp_tw_reuse – 启用处于TIME_WAIT状态的连接的重用。不建议使用。

有关更多信息,请参见手册ip(7)tcp(7)


嗨,我对服务器没有控制权,在服务器端没有任何‘文件结尾’的条件可以导致它关闭连接。 - jcm
@cm22 你可以尝试发送一些无效数据来使服务器关闭连接。我还在答案中添加了一些sysctl选项,但你需要root访问权限才能更改它们。 - abacabadabacaba
调优指南@https://gatling.io/docs/current/general/operations/有助于解决此问题,并提供详细的执行命令。添加此评论,因为该链接与@abacabadabacaba的上述答案相符。谢谢。 - Satya

0

连接池将为您解决此问题。


哎呀,这不是HTTP - 我需要连接到运行在2222端口上的服务,它使用专有协议而非HTTP :( 我通过删除200和404状态并将它们设置回专有协议响应来澄清了这个误解。 - jcm
1
你在问题中也提到了“REST”,这通常与RESTful HTTP / Web服务相关联。当然,这并不一定是必须的,就像你的例子一样。 - monex0
@jcm 我并没有提到HTTP。我不知道是什么让你认为HTTP是世界上唯一可以使用连接池的协议。 - user207421

0

我想指出,在Linux 4.12中,net.ipv4.tcp_tw_recycle已经完全被删除,参见提交95a22caee396

Linux将为每个连接随机化时间戳偏移量,使得这个选项完全失效,无论是否使用NAT。

当远程主机是一个NAT设备时,时间戳上的条件会禁止除NAT设备后面的另一台主机外的所有主机在一分钟内连接,因为它们没有共享相同的时间戳时钟。如果怀疑存在问题,则最好禁用此选项,因为它会导致难以检测和诊断的问题。

来源


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接