我是靠谱客的博主 舒适身影,最近开发中收集的这篇文章主要介绍ftp 短连接 长连接_HttpComponents HttpClient连接池(9)长连接,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

在上一篇文章里我们介绍了 httpclient 连接池对于 SSL 的支持,这里主要介绍连接池中的长连接。

根据以前文章, 对于httpclient 连接池中的池化对象 CpoolEntry 都是可以被复用的,这样在每次申请连接的时候都会从可用连接集合 available 中获取,避免每次都重新创建连接,提高了效率。关于连接池如何决定重用连接,以及连接 keep alive 保活多久的介绍,请参考这篇文章。池化对象 CpoolEntry 虽然重用了,但是里面真正的原始 socket 是长连接么?我们从使用 httpclient 的代码分析,一般使用 httpclient 的代码如下:

/*Application code*/try {      CloseableHttpClient htttpClient = HttpClients.custom().build();      HttpGet getMethod = new HttpGet("https://www.baidu.com/");      CloseableHttpResponse response = htttpClient.execute(getMethod);      HttpEntity entitry = response.getEntity();      byte[] contentArray = EntityUtils.toByteArray(entitry); } catch (Exception e) {        // TODO Handle Exception here }/*EntityUtils*/public static byte[] toByteArray(final HttpEntity entity) throws IOException {    Args.notNull(entity, "Entity");    final InputStream inStream = entity.getContent();    if (inStream == null) {       return null;    }    try {        Args.check(entity.getContentLength() <= Integer.MAX_VALUE, "HTTP entity too large to be buffered in memory");        int capacity = (int)entity.getContentLength();        if (capacity < 0) {            capacity = DEFAULT_BUFFER_SIZE;        }        final ByteArrayBuffer buffer = new ByteArrayBuffer(capacity);        final byte[] tmp = new byte[DEFAULT_BUFFER_SIZE];        int l;        while((l = inStream.read(tmp)) != -1) {            buffer.append(tmp, 0, l);        }        return buffer.toByteArray();    } finally {        inStream.close();    }}
  • 根据上述代码在调用 EntityUtils.toByteArray() 方法的时候,会最终调用 inputStream 的 close() 方法。

  • 我们 debug 调试原始代码的 inputStream 对象实例的 close() 方法。

    a0387705c92a82e61c9f0994cd443237.png

  • 通过上述调试我们发现,最终调用了 ConnectionHolder 对象的 releaseConnection() 方法,而且是重用连接的,根据以前文章, releaseConnection() 方法在重用 connection 的情况下只是把池化对象 CpoolEntry 分别在 global 连接池和 route 的 individual 连接池的正在使用集合 leased 中移除,并加入各自的可用连接集合 available 中,并没有对原始 socket 关闭,所以是 http 长连接。

  • 另外通过上述源码分析,和 EntityUtils 对象实例的 toByteArray() 方法一样, EntityUtils 对象的 toString(), consume(), consumeQuietly() 等方法也是在 finally 块里调用了 inputStream 的 close() 方法,从而间接的归还了池化对象,同时保持原始 socket 不关闭为长连接。所以,我们在程序里一定要确保 EntityUtils 中上述方法被调用。
  • 除了调用 EntityUtils 类, httpclient 也提供了 CloseableHttpResponse 的 close() 方法,我们 debug 来看查看如下:

    6283e33b51095c8284b4a4584528db2b.png

  • 通过 debug 我们发现 CloseableHttpResponse 的 close() 方法间接调用了 ConnectionHolder 的 releaseConnection() 方法,但是不重用连接。根据以前文章,该方法在不重用连接的情况下除了从 global 池和 individual route 池移除当前池化对象外,还关闭了原始 socket 。所以 response 的 close() 方法消除池化对象,没有长连接,这一点和 EntityUtils 中的方法有所区别。

根据以上分析,在 httpclient 中如果希望重用池化对象并且保持长连接,那么务必调用 EntityUtils 中的方法。如果不希望重用池化对象,不希望有长连接,那么请调用 CloseableHttpResponse 的 close() 方法。

对于使用长连接的情况下也有一些思考:
  • 如果池化对象归还到连接池并且没有关闭原始 socket 保持长连接。
  • 对于服务端可能主动关闭了这个连接,这个时候 httpclient 中的这个连接就为 close_wait 状态,下次申请到的时候是不可用的。

  • 这种情况,调用 http 请求就会失败,在失败的时候连接池会把这个连接关掉,然后利用以前文章里的重试机制重新尝试发送请求,从而解决这种问题。

  • 但是这样做并不优雅,当服务端把大量的连接关闭, httpclient 连接池中就有大量的 close_wait 状态的连接,属于未释放资源。一直到再次有连接申请,发送请求的时候才发现,然后再关闭连接释放资源,重新建立连接。

  • 所以这里推荐以前文章中介绍的启用连接清理方式,这样就避免了 close_wait 状态的未关闭连接。

目前先写到这里,在下一篇文章里我们总结一下 httpclient 连接池的使用建议。

e1529a6b0e1d453d01543db0737db284.png

最后

以上就是舒适身影为你收集整理的ftp 短连接 长连接_HttpComponents HttpClient连接池(9)长连接的全部内容,希望文章能够帮你解决ftp 短连接 长连接_HttpComponents HttpClient连接池(9)长连接所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(65)

评论列表共有 0 条评论

立即
投稿
返回
顶部