HttpClient的释放资源到底在释放什么?如何正确的释放资源?

ApacheHttpClient github地址:github.com/apache/http…

ApacheHttpClient官网地址:hc.apache.org/httpcompone…

ApacheHttpClient介绍

本文主要想聊一聊ApacheHttpClient中资源释放的那些事,如果对它不够了解的话可以参考下面的思维导图:

链接:www.processon.com/view/link/6…

ApacheHttpClient

资源类型分类

在聊这个话题前我们先要明确HttpClient中定义的资源是什么,在HttpClient中需要释放的资源可以分为四种

  1. 内存,应用层用于发送请求或接收响应

  2. 底层Sokcet

  3. Http连接,一个Http连接对应了一块用于收发数据的内存,同时绑定了一个socket。如下所示:

    image-20230326003928005

  4. Http连接池

收发数据的内存跟socket是绑定在连接对象上的,所以如果我们正常关闭了连接,那么也就完成了对这两种资源的释放

Http连接池通常我们不会关闭,如果关闭了Http连接池意味着整个HttpClient对象不能再被使用

资源释放API介绍

我们常用的资源释放的API可以分为下面几类

  1. HttpRequestBase对象的releaseConnectionabort方法,HttpGet、HttpPost都继承了这个类。

    源码如下:

    abort

    release

    这两个方法最终都会调用cancellableRef引用的对象的cancel方法,cancellableRef在不同的时机对应不同的资源对象:ConnectionRequest,代表一个从连接池中获取连接的请求。ConnectionHolder,代表一个真实的http连接

    代码位于:org.apache.http.impl.execchain.MainClientExec#execute

    image-20230325235403131

    当调用ConnectionRequest的cancel方法时,代表取消本次获取连接的请求。当调用ConnectionHolder的cancel方法时,代表什么呢?代码如下:org.apache.http.impl.execchain.ConnectionHolder#abortConnection

    image-20230326000714850

    上面代码主要做了两件事:关闭socket连接,归还连接到连接池。可能有的小伙伴会有疑问,为什么把连接绑定的socket关闭了还要将连接归还到连接池呢?这是因为连接池中的连接数量是有限制的,假设我们设置设置了最大连接数为10,如果不把这个连接归还到连接池,那么它会一直占用一个连接名额直到被驱逐策略所驱逐。并且在调用连接池的releaseConnection方法时,会判断归还的连接是否可用,如果不可用会直接从连接池中移除这个对象

    代码:org.apache.http.impl.conn.PoolingHttpClientConnectionManager#releaseConnection

    image-20230326001731861

  2. CloseableHttpResponse对象的close方法

    最终会调用org.apache.http.impl.conn.PoolingHttpClientConnectionManager#releaseConnection

    image-20230326003013159

    可以看到,当调用response对象的close方法,会关闭socket,同时释放连接到连接池。这里顺便说一下,连接的close方法跟shutdown方法的区别在于,close方法会尝试将未写完的数据全部写入server端,而shutdown会直接丢弃

  3. EntityUtils的consume方法跟toString方法以及关闭HttpEntity中的流

    EntityUtils的consume、toString最终都是调用HttpEntity.getContent().close方法,实际都是在关闭HttpEntity中的流

    最终会调用:org.apache.http.impl.execchain.ResponseEntityProxy#streamClosed

    image-20230326005604357

    wrapped.close()最终会调用org.apache.http.impl.io.ContentLengthInputStream#close,代码如下:image-20230326005915733

    可以看到,它只是简单的读取数据但没有进行其它任何操作,这实际是为了释放我们在前文提到了内存。这是为了安全的复用连接,我们需要将上次请求接收并缓存的所有数据清理干净,否则下次使用这个连接时可能读取到上次请求没有读取完的数据

  4. CloseableHttpClient对象的close方法

    这个方法会关闭所有HttpClient对象涉及的资源,包括连接池,HttpClient对象建议全局共享一个,因此一般不会调用

正确的释放资源

通过上面的分析,正确的资源释放姿势如下:

private static final CloseableHttpClient httpclient = HttpClients.createDefault();
public static void main(String[] args) throws Exception {
    
    
  try {
    
    
    HttpGet httpGet = new HttpGet("http://httpbin.org/get");
    CloseableHttpResponse response = httpclient.execute(httpGet);
    try {
    
    
      HttpEntity entity1 = response.getEntity();

      // 确保内存完成释放
      final String json = EntityUtils.toString(entity1);
      // 完成业务处理....
      
    }catch(){
    
    
      // 异常处理....
    } 
    finally {
    
    
      // 保证连接释放,如果EntityUtils.toString调用成功,这个方法实际上不会做任何操作
      response.close();
    }
  }

猜你喜欢

转载自blog.csdn.net/qq_34626094/article/details/130517338