Java Jersey服务器关闭\u客户端读取超时后等待泄漏

Java Jersey服务器关闭\u客户端读取超时后等待泄漏,java,rest,tcp,jersey,socket-timeout-exception,Java,Rest,Tcp,Jersey,Socket Timeout Exception,以下是使用Jersey从客户机通信到REST服务器的(大大简化的)代码。在开始时建立连接有5分钟的超时,从底层套接字读取有2分钟的超时 public class JerseyClient { private final Map<Action, WebResource> resources; public JerseyClient(URI uri) { this.resources = new EnumMap<Action, WebResource>(A

以下是使用
Jersey
从客户机通信到
REST
服务器的(大大简化的)代码。在开始时建立连接有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");
      }
    }
  }
}
公共类Jersey客户端{
私人最终地图资源;
公共运动衫客户端(URI){
this.resources=newenummap(Action.class);
this.resources.put(Action.put,getClient().resource(Action.put.getURI());
this.resources.put(Action.Query,getClient().resource(Action.Query.getURI());
}
私有字符串提交(操作、字符串输入)引发异常{
WebResource=this.resources.get(操作);
ClientResponse-response=null;
已同步(资源){
试一试{
response=resource.accept(MediaType.APPLICATION\u JSON\u类型,
MediaType.TEXT\u PLAIN\u TYPE).TYPE(MediaType.APPLICATION\u JSON\u TYPE)。
post(ClientResponse.class,输入);
字符串responseString=null;
//处理响应并生成响应字符串。。。
回报率;
}最后{
if(响应!=null){
response.close();
}
}
}
}
私有静态客户端getClient(){
Client=Client.create();
客户端.setReadTimeout(2*60*1000);
client.setConnectTimeout(5*60*1000);
返回客户;
}
私有枚举操作{
放置、查询;
公共URI getURI(){
开关(本){
案件付诸表决:
返回URI.create(“PUT_URI”);
个案查询:
返回URI.create(“查询URI”);
违约:
抛出新的InvalidStateException(“非法操作”);
}
}
}
}
除非在客户端触发读取超时,否则上述代码将按预期工作。在这种情况下,会抛出一个
SocketTimeoutException
,因此上面
JerseyClient
类的
submit()
方法中的响应对象保持
null
,因此底层套接字永远不会完全关闭

显然,客户端部分关闭了套接字,因为在另一端,服务器进入
CLOSE\u WAIT
状态(即,根据
TCP
规范,它已从客户端接收到
FIN
数据包)。但是,由于它从未从客户端获得最终的
ACK
(如果调用了
response.close()
,则应发送该确认),因此它将连接保持在
close\u WAIT
(如
netstat
所示),因此,来自客户端的每个超时的
REST
调用都可能在服务器上创建一个悬空的
CLOSE\u WAIT


在不完全重新设计上述代码的情况下,有没有办法解决这个问题?

您的描述毫无意义。状态的名称是CLOSE\u WAIT,而不是CLOSED\u WAIT,它的意思是它(正确的)名称所表达的:它在接收到来自对等方的远程关闭后,正在等待本地应用程序关闭套接字

如果您的服务器正在进入关闭\u等待:

  • 客户端已关闭套接字
  • 服务器尚未关闭套接字。这是服务器中的一个错误
  • 服务器在关闭套接字发出FIN之前,永远不会从客户端获得最终ACK。ACK是对FIN的确认。在服务器发出FIN之前,客户机无法确认任何内容
  • 是关闭使其脱离关闭等待,而不是客户端确认
  • 在客户端调用
    response.close()
    ,与客户端发送最终ACK无关

  • 你的描述毫无意义。状态的名称是CLOSE\u WAIT,而不是CLOSED\u WAIT,它的意思是它(正确的)名称所表达的:它在接收到来自对等方的远程关闭后,正在等待本地应用程序关闭套接字

    如果您的服务器正在进入关闭\u等待:

  • 客户端已关闭套接字
  • 服务器尚未关闭套接字。这是服务器中的一个错误
  • 服务器在关闭套接字发出FIN之前,永远不会从客户端获得最终ACK。ACK是对FIN的确认。在服务器发出FIN之前,客户机无法确认任何内容
  • 是关闭使其脱离关闭等待,而不是客户端确认
  • 在客户端调用
    response.close()
    ,与客户端发送最终ACK无关

  • 你当然是对的,等等。这个问题写在一本哈利字典里,现在已经更正了。场景仍如最初所述。谢谢和+1.:-)这种情况下,服务器没有关闭套接字,而客户端关闭了套接字,这与最初描述的完全相反,尽管您进行了一些小的编辑,但现在仍然在描述。我们正在根据一个事实跟踪问题,即存在多个CLOSE_WAIT连接。无论谁做了什么,根据文档,都需要在客户端调用
    response.close()
    。在上面的代码中,有没有办法做到这一点?这是主要问题。谢谢。这是个新问题,我不明白。上述代码中没有可访问的套接字。它由基础库根据需要创建和关闭。它为POST打开,在收到响应或错误后关闭。因此,在代码中没有明显的地方,您可能希望提前或之后关闭套接字,或者不管您未声明的需求是什么。你应该考虑赞成或接受帮助你的答案。我建议你重新开始一个新的问题,新的证据,新的一切。这个问题是徒劳的。你说的“等一下”当然是对的。这个问题写在一本哈利字典里,现在已经更正了。场景仍如最初所述。谢谢和+1.:-)情况是服务器尚未关闭