聊聊httpclient的ConnectionHolder

本文主要研究一下httpclient的ConnectionHolder

ConnectionReleaseTrigger

org/apache/http/conn/ConnectionReleaseTrigger.java

复制代码
/**
 * Interface for releasing a connection. This can be implemented by various
 * "trigger" objects which are associated with a connection, for example
 * a {@link EofSensorInputStream} or the {@link ManagedHttpClientConnection} itself.
 * <p>
 * The methods in this interface can safely be called multiple times.
 * The first invocation releases the connection, subsequent calls
 * are ignored.
 *
 * @since 4.0
 */
public interface ConnectionReleaseTrigger {

    /**
     * Releases the connection with the option of keep-alive. This is a
     * "graceful" release and may cause IO operations for consuming the
     * remainder of a response entity. Use
     * {@link #abortConnection abortConnection} for a hard release. The
     * connection may be reused as specified by the duration.
     *
     * @throws IOException
     *             in case of an IO problem. The connection will be released
     *             anyway.
     */
    void releaseConnection()
        throws IOException;

    /**
     * Releases the connection without the option of keep-alive.
     * This is a "hard" release that implies a shutdown of the connection.
     * Use {@link #releaseConnection()} for a graceful release.
     *
     * @throws IOException      in case of an IO problem.
     *         The connection will be released anyway.
     */
    void abortConnection()
        throws IOException;

}

ConnectionReleaseTrigger定义了releaseConnection、abortConnection方法

ConnectionHolder

org/apache/http/impl/execchain/ConnectionHolder.java

复制代码
@Contract(threading = ThreadingBehavior.SAFE)
class ConnectionHolder implements ConnectionReleaseTrigger, Cancellable, Closeable {

    private final Log log;

    private final HttpClientConnectionManager manager;
    private final HttpClientConnection managedConn;
    private final AtomicBoolean released;
    private volatile boolean reusable;
    private volatile Object state;
    private volatile long validDuration;
    private volatile TimeUnit timeUnit;

    public ConnectionHolder(
            final Log log,
            final HttpClientConnectionManager manager,
            final HttpClientConnection managedConn) {
        super();
        this.log = log;
        this.manager = manager;
        this.managedConn = managedConn;
        this.released = new AtomicBoolean(false);
    }

    public boolean isReusable() {
        return this.reusable;
    }

    public void markReusable() {
        this.reusable = true;
    }

    public void markNonReusable() {
        this.reusable = false;
    }

    public void setState(final Object state) {
        this.state = state;
    }

    public void setValidFor(final long duration, final TimeUnit timeUnit) {
        synchronized (this.managedConn) {
            this.validDuration = duration;
            this.timeUnit = timeUnit;
        }
    }

    private void releaseConnection(final boolean reusable) {
        if (this.released.compareAndSet(false, true)) {
            synchronized (this.managedConn) {
                if (reusable) {
                    this.manager.releaseConnection(this.managedConn,
                            this.state, this.validDuration, this.timeUnit);
                } else {
                    try {
                        this.managedConn.close();
                        log.debug("Connection discarded");
                    } catch (final IOException ex) {
                        if (this.log.isDebugEnabled()) {
                            this.log.debug(ex.getMessage(), ex);
                        }
                    } finally {
                        this.manager.releaseConnection(
                                this.managedConn, null, 0, TimeUnit.MILLISECONDS);
                    }
                }
            }
        }
    }

    @Override
    public void releaseConnection() {
        releaseConnection(this.reusable);
    }

    @Override
    public void abortConnection() {
        if (this.released.compareAndSet(false, true)) {
            synchronized (this.managedConn) {
                try {
                    this.managedConn.shutdown();
                    log.debug("Connection discarded");
                } catch (final IOException ex) {
                    if (this.log.isDebugEnabled()) {
                        this.log.debug(ex.getMessage(), ex);
                    }
                } finally {
                    this.manager.releaseConnection(
                            this.managedConn, null, 0, TimeUnit.MILLISECONDS);
                }
            }
        }
    }

    @Override
    public boolean cancel() {
        final boolean alreadyReleased = this.released.get();
        log.debug("Cancelling request execution");
        abortConnection();
        return !alreadyReleased;
    }

    public boolean isReleased() {
        return this.released.get();
    }

    @Override
    public void close() throws IOException {
        releaseConnection(false);
    }

}

ConnectionHolder实现了ConnectionReleaseTrigger接口,它提供了isReusable、markReusable、markNonReusable、setValidFor、releaseConnection、abortConnection、cancel、close等方法,其中releaseConnection是依赖reusable属性来决定是关闭连接还是归还连接,而abortConnection方法直接managedConn.shutdown(),然后在执行manager.releaseConnection

releaseConnection在连接复用则执行manager.releaseConnection,连接不复用先执行managedConn.close()再执行manager.releaseConnection(validDuratioin为0);

abortConnection先执行this.managedConn.shutdown(),再执行manager.releaseConnection(validDuratioin为0);

close与shutdown的区别在于close是优雅关闭,会试图flush内部的output buffer,shutdown是强制关闭,不会试图去flush内部的buffer

MainClientExec

org/apache/http/impl/execchain/MainClientExec.java

复制代码
    public CloseableHttpResponse execute(
            final HttpRoute route,
            final HttpRequestWrapper request,
            final HttpClientContext context,
            final HttpExecutionAware execAware) throws IOException, HttpException {

        		//......

                // The connection is in or can be brought to a re-usable state.
                if (reuseStrategy.keepAlive(response, context)) {
                    // Set the idle duration of this connection
                    final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
                    if (this.log.isDebugEnabled()) {
                        final String s;
                        if (duration > 0) {
                            s = "for " + duration + " " + TimeUnit.MILLISECONDS;
                        } else {
                            s = "indefinitely";
                        }
                        this.log.debug("Connection can be kept alive " + s);
                    }
                    connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
                    connHolder.markReusable();
                } else {
                    connHolder.markNonReusable();
                }

                //......     

            // check for entity, release connection if possible
            final HttpEntity entity = response.getEntity();
            if (entity == null || !entity.isStreaming()) {
                // connection not needed and (assumed to be) in re-usable state
                connHolder.releaseConnection();
                return new HttpResponseProxy(response, null);
            }
            return new HttpResponseProxy(response, connHolder);

            //......                   
    }        

MainClientExec的execute方法在通过requestExecutor.execute(request, managedConn, context)获取到response的时候,会根据reuseStrategy.keepAlive(response, context)来判断该conn是reusable还是nonReusable的,最后在成功场景,entity为null或者不是streaming的会执行connHolder.releaseConnection(),异常场景执行connHolder.abortConnection()

DefaultClientConnectionReuseStrategy

org/apache/http/impl/client/DefaultClientConnectionReuseStrategy.java

复制代码
public class DefaultClientConnectionReuseStrategy extends DefaultConnectionReuseStrategy {

    public static final DefaultClientConnectionReuseStrategy INSTANCE = new DefaultClientConnectionReuseStrategy();

    @Override
    public boolean keepAlive(final HttpResponse response, final HttpContext context) {

        final HttpRequest request = (HttpRequest) context.getAttribute(HttpCoreContext.HTTP_REQUEST);
        if (request != null) {
            final Header[] connHeaders = request.getHeaders(HttpHeaders.CONNECTION);
            if (connHeaders.length != 0) {
                final TokenIterator ti = new BasicTokenIterator(new BasicHeaderIterator(connHeaders, null));
                while (ti.hasNext()) {
                    final String token = ti.nextToken();
                    if (HTTP.CONN_CLOSE.equalsIgnoreCase(token)) {
                        return false;
                    }
                }
            }
        }
        return super.keepAlive(response, context);
    }

}

DefaultClientConnectionReuseStrategy继承了DefaultConnectionReuseStrategy,其keepAlive先判断response的header有无Connection: close,没有则返回false,有则调用super.keepAlive进行进一步判断(`对于http1.0的返回false)

小结

httpclient的ConnectionHolder实现了ConnectionReleaseTrigger接口,它提供了isReusable、markReusable、markNonReusable、setValidFor、releaseConnection、abortConnection、cancel、close等方法;releaseConnection在连接复用则执行manager.releaseConnection(validDuration为response的Keep-Alive返回的timeout参数,若没有则为-1),连接不复用先执行managedConn.close()再执行manager.releaseConnection(validDuration为0);abortConnection先执行this.managedConn.shutdown(),再执行manager.releaseConnection(validDuration为0);

MainClientExec的execute方法在通过requestExecutor.execute(request, managedConn, context)获取到response的时候,会根据reuseStrategy.keepAlive(response, context)来判断该conn是reusable还是nonReusable的,最后在成功场景,entity为null或者不是streaming的会执行connHolder.releaseConnection(),异常场景执行connHolder.abortConnection()
DefaultClientConnectionReuseStrategy继承了DefaultConnectionReuseStrategy,其keepAlive先判断response的header有无Connection: close,没有则返回false,有则调用super.keepAlive进行进一步判断(`对于http1.0的返回false)
close与shutdown的区别在于close是优雅关闭,会试图flush内部的output buffer,shutdown是强制关闭,不会试图去flush内部的buffer

相关推荐
柳贯一(逆流河版)1 小时前
Spring 三级缓存:破解循环依赖的底层密码
java·spring·缓存·bean的循环依赖
该用户已不存在3 小时前
OpenJDK、Temurin、GraalVM...到底该装哪个?
java·后端
TT哇3 小时前
@[TOC](计算机是如何⼯作的) JavaEE==网站开发
java·redis·java-ee
Tina学编程3 小时前
48Days-Day19 | ISBN号,kotori和迷宫,矩阵最长递增路径
java·算法
青川入梦3 小时前
MyBatis极速通关上篇:Spring Boot环境搭建+用户管理实战
java·开发语言·mybatis
执子手 吹散苍茫茫烟波4 小时前
leetcode415. 字符串相加
java·leetcode·字符串
小韩博4 小时前
网络安全(Java语言)脚本 汇总(二)
java·安全·web安全
萤丰信息4 小时前
技术赋能安全:智慧工地构建城市建设新防线
java·大数据·开发语言·人工智能·智慧城市·智慧工地
带刺的坐椅4 小时前
Java MCP 的鉴权?好简单的啦
java·鉴权·mcp·solon-ai
Pocker_Spades_A4 小时前
飞算JavaAI家庭记账系统:从收支记录到财务分析的全流程管理方案
java·开发语言