CloseableHttpResponse.close() 和 httpPost.releaseConnection() 有什么区别?


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();
}
  1. CloseableHttpResponse.close()关闭 TCP 套接字
  2. HttpPost.releaseConnection()关闭 TCP 套接字
  3. EntityUtils.consume(response.getEntity())允许您重复使用 TCP 套接字

CloseableHttpResponse.close()关闭 TCP 套接字,防止重新使用连接。您需要建立新的 TCP 连接才能发起另一个请求。

这是导致我得出上述结论的调用链:

  1. HttpResponseProxy.close()
  2. -> ConnectionHolder.close()
  3. -> ConnectionHolder.releaseConnection(reuseable=false)
  4. -> managedConn.close()
  5. -> BHttpConnectionBase.close()
  6. -> Socket.close()

HttpPost.releaseConnection()也会关闭套接字。这是导致我得出上述结论的调用链:

  1. HttpPost.releaseConnection()
  2. HttpRequestBase.releaseConnect()
  3. AbstractExecutionAwareRequest.reset()
  4. ConnectionHolder.cancel() (
  5. ConnectionHolder.abortConnection()
  6. 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建议的操作并使用实体。

最新更新