CloseableHttpResponse response = null;
try {
// do some thing ....
HttpPost request = new HttpPost("some url");
response = getHttpClient().execute(request);
// do some other thing ....
} catch(Exception e) {
// deal with exception
} finally {
if(response != null) {
try {
response.close(); // (1)
} catch(Exception e) {}
request.releaseConnection(); // (2)
}
}
我已经提出了像上面这样的 http 请求。
为了释放底层连接,调用 (1) 和 (2) 是否正确? 两次调用有什么区别?
简短回答:
request.releaseConnection()
正在释放基础 HTTP 连接以允许重用它。 response.close()
关闭流(而不是连接),则此流是我们从网络套接字流式传输的响应内容。
长答案:
在任何最新版本> 4.2 中,甚至可能在此之前,要遵循的正确模式是 不要使用 releaseConnection
.
request.releaseConnection()
释放底层的httpConnection,因此请求可以重用,但是Java文档说:
我们不是释放连接,而是确保一种简化从 HttpClient 3.1 API 迁移的便捷方法...
响应内容被完全使用,从而确保连接被释放并准备好重用。下面显示了一个简短的示例:
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://targethost/homepage");
CloseableHttpResponse response1 = httpclient.execute(httpGet);
try {
System.out.println(response1.getStatusLine());
HttpEntity entity1 = response1.getEntity();
// do something useful with the response body
String bodyAsString = EntityUtils.toString(exportResponse.getEntity());
System.out.println(bodyAsString);
// and ensure it is fully consumed (this is how stream is released.
EntityUtils.consume(entity1);
} finally {
response1.close();
}
-
CloseableHttpResponse.close()
关闭 TCP 套接字 -
HttpPost.releaseConnection()
关闭 TCP 套接字 -
EntityUtils.consume(response.getEntity())
允许您重复使用 TCP 套接字
详
CloseableHttpResponse.close()
关闭 TCP 套接字,防止重新使用连接。您需要建立新的 TCP 连接才能发起另一个请求。
这是导致我得出上述结论的调用链:
- HttpResponseProxy.close()
- -> ConnectionHolder.close()
- -> ConnectionHolder.releaseConnection(reuseable=false)
- -> managedConn.close()
- -> BHttpConnectionBase.close()
- -> Socket.close()
HttpPost.releaseConnection()
也会关闭套接字。这是导致我得出上述结论的调用链:
- HttpPost.releaseConnection()
- HttpRequestBase.releaseConnect()
- AbstractExecutionAwareRequest.reset()
- ConnectionHolder.cancel() (
- ConnectionHolder.abortConnection()
- HttpConnection.shutdown()
下面是实验性代码,也演示了上述三个事实:
import java.lang.reflect.Constructor;
import java.net.Socket;
import java.net.SocketImpl;
import java.net.SocketImplFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
public class Main {
private static SocketImpl newSocketImpl() {
try {
Class<?> defaultSocketImpl = Class.forName("java.net.SocksSocketImpl");
Constructor<?> constructor = defaultSocketImpl.getDeclaredConstructor();
constructor.setAccessible(true);
return (SocketImpl) constructor.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
// this is a hack that lets me listen to Tcp socket creation
final List<SocketImpl> allSockets = Collections.synchronizedList(new ArrayList<>());
Socket.setSocketImplFactory(new SocketImplFactory() {
public SocketImpl createSocketImpl() {
SocketImpl socket = newSocketImpl();
allSockets.add(socket);
return socket;
}
});
System.out.println("num of sockets after start: " + allSockets.size());
CloseableHttpClient client = HttpClientBuilder.create().build();
System.out.println("num of sockets after client created: " + allSockets.size());
HttpGet request = new HttpGet("http://www.google.com");
System.out.println("num of sockets after get created: " + allSockets.size());
CloseableHttpResponse response = client.execute(request);
System.out.println("num of sockets after get executed: " + allSockets.size());
response.close();
System.out.println("num of sockets after response closed: " + allSockets.size());
response = client.execute(request);
System.out.println("num of sockets after request executed again: " + allSockets.size());
request.releaseConnection();
System.out.println("num of sockets after release connection: " + allSockets.size());
response = client.execute(request);
System.out.println("num of sockets after request executed again for 3rd time: " + allSockets.size());
EntityUtils.consume(response.getEntity());
System.out.println("num of sockets after entityConsumed: " + allSockets.size());
response = client.execute(request);
System.out.println("num of sockets after request executed again for 4th time: " + allSockets.size());
}
}
绒球.xml
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.joseph</groupId>
<artifactId>close.vs.release.conn</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<build>
<plugins>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies>
</project>
输出:
num of sockets after start: 0
num of sockets after client created: 0
num of sockets after get created: 0
num of sockets after get executed: 1
num of sockets after response closed: 1
num of sockets after request executed again: 2
num of sockets after release connection: 2
num of sockets after request executed again for 3rd time: 3
num of sockets after entityConsumed: 3
num of sockets after request executed again for 4th time: 3
请注意,.close()
和 .releaseConnection()
都会导致新的 tcp 连接。仅使用实体才允许您重复使用 tcp 连接。
如果希望在每次请求后连接可重复使用,则需要执行@Matt建议的操作并使用实体。