简体   繁体   English

客户端读取超时后,泽西服务器CLOSE_WAIT泄漏

[英]Jersey server CLOSE_WAIT leak after read timeout at the client

Following is the (greatly simplified) code used for communicating from a client machine, to a REST server, using Jersey . 以下是(大大简化的)代码,用于使用Jersey从客户端计算机与REST服务器进行通信。 There is a 5 minute timeout for establishing a connection at the start, and a 2 minute timeout for reading from the underlying socket. 在开始时建立连接有5分钟的超时时间,而从底层套接字读取有2分钟的超时时间。

public class JerseyClient {

  private final Map<Action, WebResource> resources;

  public JerseyClient(URI uri) {
    this.resources = new EnumMap<Action, WebResource>(Action.class);
    this.resources.put(Action.Put, getClient().resource(Action.Put.getURI()));
    this.resources.put(Action.Query, getClient().resource(Action.Query.getURI()));
  }

  private String submit(Action action, String input) throws Exception {
    WebResource resource = this.resources.get(action);
    ClientResponse response = null;
    synchronized (resource) {
      try {
          response = resource.accept(MediaType.APPLICATION_JSON_TYPE,
                  MediaType.TEXT_PLAIN_TYPE).type(MediaType.APPLICATION_JSON_TYPE).
                  post(ClientResponse.class, input);
          String responseString = null;
          // Handle the response and produce a response string...
        return responseString;
      } finally {
        if (response != null) {
          response.close();
        }
      }
    }
  }

  private static Client getClient() {
    Client client = Client.create();
    client.setReadTimeout(2*60*1000);
    client.setConnectTimeout(5*60*1000);
    return client;
  }

  private enum Action {
    Put, Query;
    public URI getURI(){
      switch (this) {
        case Put:
          return URI.create("PUT_URI");
        case Query:
          return URI.create("QUERY_URI");
        default:
          throw new InvalidStateException("Illegal action");
      }
    }
  }
}

The above code works as expected, unless the read timeout triggers at the client. 除非读取超时在客户端触发,否则以上代码按预期工作。 In that case, a SocketTimeoutException is thrown and thus the response object in the submit() method of the JerseyClient class above remains null , so the underlying socket is never closed fully. 在这种情况下,将抛出SocketTimeoutException ,因此上述JerseyClient类的JerseyClient submit()方法中的响应对象仍为null ,因此基础套接字永远不会完全关闭。

Apparently there is a partial closure of the socket by the client since, on the other side, the server enters the CLOSE_WAIT state (ie, it has received a FIN packet from the client, as per the TCP specification). 显然,客户端会部分关闭套接字,因为另一方面,服务器会进入CLOSE_WAIT状态(即,它已根据TCP规范从客户端接收到FIN数据包)。 However, since it never gets the final ACK from the client (which should be sent if response.close() was called), it keeps the connection at CLOSE_WAIT (as shown by netstat ), so each timed out REST call from the client potentially creates a dangling CLOSE_WAIT on the server. 但是,由于它永远不会从客户端获取最终的ACK (如果调用response.close() ,则应发送该最终ACK ),因此它将连接保持在CLOSE_WAIT (如netstat所示),因此每次从客户端超时的REST调用都可能在服务器上创建一个悬空的CLOSE_WAIT

Is there any way of solving the issue, without completely re-engineering the above code? 有没有解决问题的方法,而无需完全重新设计以上代码?

Your description doesn't make sense. 您的描述没有道理。 The name of the state is CLOSE_WAIT, not CLOSED_WAIT, and it means what its (correct) name expresses: it is waiting for the local application to close the socket, after receiving a remote close from the peer. 状态的名称为CLOSE_WAIT,而不是CLOSED_WAIT,这表示状态(正确)的名称表示:从对等方收到远程关闭后,它正在等待本地应用程序关闭套接字。

If your server is entering CLOSE_WAIT: 如果您的服务器正在输入CLOSE_WAIT:

  1. The client has closed the socket. 客户端关闭套接字。
  2. The server has not closed the socket. 服务器尚未关闭套接字。 This is a bug in the server. 这是服务器中的错误。
  3. The server will never get a final ACK from the client until it issues a FIN by closing the socket. 在服务器通过关闭套接字发出FIN之前,服务器永远不会从客户端获得最终的ACK。 The ACK is an acknowledgement of the FIN. ACK是FIN的确认。 Until the server issues the FIN there is nothing for the client to ACK. 在服务器发出FIN之前,客户端没有任何内容可进行ACK。
  4. It is the close that gets it out of CLOSE_WAIT, not the client ACK. 是关闭使它退出CLOSE_WAIT,而不是客户端ACK。
  5. Calling response.close() at the client has nothing to do with the client sending the final ACK. 在客户端调用response.close()与客户端发送最终ACK无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM