[英]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:
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.