概述
根据以前文章, 对于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() 方法。
通过上述调试我们发现,最终调用了 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 来看查看如下:
通过 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 状态的未关闭连接。
最后
以上就是舒适身影为你收集整理的ftp 短连接 长连接_HttpComponents HttpClient连接池(9)长连接的全部内容,希望文章能够帮你解决ftp 短连接 长连接_HttpComponents HttpClient连接池(9)长连接所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复