Java 服务端架构
Spring、Netty、日志框架与工程化实战
🎨 视觉封面Httpcomponents-client 连接释放逻辑源码研究
研究背景
上篇(Httpcomponents-core 从池中获取连接代码解析)研究中,我们知道,Httpcomponents-client底层维护了一个socket连接池,对于同一个地址,默认只可以建立2个连接。如果连接不及时释放,就会造成连接池中无可用的连接获取,从而导致请求等待(阻塞)。因此,我们需要关注连接池释放的逻辑。
释放方式
一个比较常见的方式就通过EntityUtils类,比如EntityUtils.consume,源码如下:
/**
* Ensures that the entity content is fully consumed and the content stream, if exists,
* is closed.
*
* @param entity the entity to consume.
* @throws IOException if an error occurs reading the input stream
*
* @since 4.1
*/
public static void consume(final HttpEntity entity) throws IOException {
if (entity == null) {
return;
}
if (entity.isStreaming()) {
final InputStream instream = entity.getContent();
if (instream != null) {
instream.close();
}
}
}
源码解析
上述代码,看上去似乎跟连接的释放没什么关系,实际上,这里的InputStream的实现类是httpcomponents-client里封装的, EofSensorInputStream 。而该Inputstream在构造的时候,会添加一个watcher,该watcher会监听流的消费,从而释放连接。4.3版本以后HttpEntity的实现类变为ResponseEntityProxy,其从Entity中获取Content的代码逻辑如下:
@Override
public InputStream getContent() throws IOException {
return new EofSensorInputStream(this.wrappedEntity.getContent(), this);
}
因此,在EofSensorInputStream关闭流的时候会有响应的处理:
@Override
public void close() throws IOException {
// tolerate multiple calls to close()
selfClosed = true;
checkClose();
}
protected void checkClose() throws IOException {
final InputStream toCloseStream = wrappedStream;
if (toCloseStream != null) {
try {
boolean scws = true; // should close wrapped stream?
if (eofWatcher != null) {
scws = eofWatcher.streamClosed(toCloseStream);
}
if (scws) {
toCloseStream.close();
}
} finally {
wrappedStream = null;
}
}
}
可见,在关闭流的时候,会回调watcher的streamCloased方法,在方法中会对连接进行释放:
@Override
public boolean streamClosed(final InputStream wrapped) throws IOException {
try {
final boolean open = connHolder != null && !connHolder.isReleased();
// this assumes that closing the stream will
// consume the remainder of the response body:
try {
if (wrapped != null) {
wrapped.close();
}
releaseConnection();
} catch (final SocketException ex) {
if (open) {
throw ex;
}
}
} catch (final IOException ex) {
abortConnection();
throw ex;
} catch (final RuntimeException ex) {
abortConnection();
throw ex;
} finally {
cleanup();
}
return false;
}
总结
至此,已经基本理解了HttpClient释放连接的方式,就是在他封装的Entity流上,增加一个watcher,只要调用了stream的close方法,就会自动释放连接。所以,你可见到在EntityUtil中的各种消费Entity的函数中都会去主动关闭流。这也是为什么我们强调在使用HttpClient的过程中,即使Response不使用也一定要消费掉,因为不消费会导致连接永远不会释放。最终造成连接池耗尽造成阻塞。
所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI
欢迎加入:C++ GESP/CSP 考级答疑群(688906745) 与 Java/Python交流群(982860385),点击可直接加群。
猜你想读 · 相关文章推荐
Httpcomponents-core 从池中获取连接代码解析
最近使用Httpcomponents-client过程中遇到一个线程阻塞的问题。通过jstack dump线程发现,是block在AbstractConnPool的getPoolEntryBlocking中,于是决定研究一下Httpcomponents-client的源码。 <!--break-- 问题背景 邮件发送线...
Poj1001 求高精度幂
实话说,不是正规解法,只是因为用的Java,利用里里面的API而已。仅供一笑了。 PS:代码是AC过的。
Java JNI Windows64位系统下 使用32位的dll
<p 今天遇到在处理一个多classloader调用本地native方法报错的问题的时候,想要通过调用本地的一个dll进行测试。该dll是在32位环境下编译的。而<a href="http://www.coderli.com"OneCoder</a的调试机器是64位的win7。自然调用会报如下错误:</p <block...
OneCoder (lihongzheshuai)
一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com