OneCoder Avatar
OneCodercoderli.com · 960 篇博文
{ } Java

Java 服务端架构

Spring、Netty、日志框架与工程化实战

🎨 视觉封面

Httpcomponents-client 连接释放逻辑源码研究

📅 2017-10-11·✍️ onecoder·计算中...·⏱️ 8 分钟
#HttpClient#Java

研究背景

上篇(Httpcomponents-core 从池中获取连接代码解析)研究中,我们知道,Httpcomponents-client底层维护了一个socket连接池,对于同一个地址,默认只可以建立2个连接。如果连接不及时释放,就会造成连接池中无可用的连接获取,从而导致请求等待(阻塞)。因此,我们需要关注连接池释放的逻辑。

释放方式

一个比较常见的方式就通过EntityUtils类,比如EntityUtils.consume,源码如下:

Java 20 行
/**
     * 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的代码逻辑如下:

Java 4 行
 @Override
    public InputStream getContent() throws IOException {
        return new EofSensorInputStream(this.wrappedEntity.getContent(), this);
    }

因此,在EofSensorInputStream关闭流的时候会有响应的处理:

Java 24 行
 @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方法,在方法中会对连接进行释放:

Java 27 行
@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不使用也一定要消费掉,因为不消费会导致连接永远不会释放。最终造成连接池耗尽造成阻塞。

💡 OneCoder 资源指引

所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI

🤝 技术交流与答疑

欢迎加入:C++ GESP/CSP 考级答疑群(688906745)Java/Python交流群(982860385),点击可直接加群。

📚

猜你想读 · 相关文章推荐

OneCoder

OneCoder (lihongzheshuai)

一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com

读者讨论与留言

0 条讨论
✨ 支持点击留言直接回复 · Markdown 引用格式
💬 还没有读者留言,快来成为第一个讨论者吧!