美文网首页
聊聊httpclient的ConnectionHolder

聊聊httpclient的ConnectionHolder

作者: go4it | 来源:发表于2023-11-27 09:12 被阅读0次

    本文主要研究一下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

    相关文章

      网友评论

          本文标题:聊聊httpclient的ConnectionHolder

          本文链接:https://www.haomeiwen.com/subject/shyzwdtx.html