美文网首页
聊聊httpclient的connect

聊聊httpclient的connect

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

    本文主要研究一下httpclient的connect

    HttpClientConnectionOperator

    org/apache/http/conn/HttpClientConnectionOperator.java

    public interface HttpClientConnectionOperator {
    
        void connect(
                ManagedHttpClientConnection conn,
                HttpHost host,
                InetSocketAddress localAddress,
                int connectTimeout,
                SocketConfig socketConfig,
                HttpContext context) throws IOException;
    
        void upgrade(
                ManagedHttpClientConnection conn,
                HttpHost host,
                HttpContext context) throws IOException;
    
    }
    

    HttpClientConnectionOperator定义了connect及upgrade方法,它有一个默认的实现类为DefaultHttpClientConnectionOperator

    DefaultHttpClientConnectionOperator

    org/apache/http/impl/conn/DefaultHttpClientConnectionOperator.java

    public class DefaultHttpClientConnectionOperator implements HttpClientConnectionOperator {
    
        static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry";
    
        private final Log log = LogFactory.getLog(getClass());
    
        private final Lookup<ConnectionSocketFactory> socketFactoryRegistry;
        private final SchemePortResolver schemePortResolver;
        private final DnsResolver dnsResolver;
    
        public DefaultHttpClientConnectionOperator(
                final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
                final SchemePortResolver schemePortResolver,
                final DnsResolver dnsResolver) {
            super();
            Args.notNull(socketFactoryRegistry, "Socket factory registry");
            this.socketFactoryRegistry = socketFactoryRegistry;
            this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
                DefaultSchemePortResolver.INSTANCE;
            this.dnsResolver = dnsResolver != null ? dnsResolver :
                SystemDefaultDnsResolver.INSTANCE;
        }
    
        //......
    
        public void connect(
                final ManagedHttpClientConnection conn,
                final HttpHost host,
                final InetSocketAddress localAddress,
                final int connectTimeout,
                final SocketConfig socketConfig,
                final HttpContext context) throws IOException {
            final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context);
            final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
            if (sf == null) {
                throw new UnsupportedSchemeException(host.getSchemeName() +
                        " protocol is not supported");
            }
            final InetAddress[] addresses = host.getAddress() != null ?
                    new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName());
            final int port = this.schemePortResolver.resolve(host);
            for (int i = 0; i < addresses.length; i++) {
                final InetAddress address = addresses[i];
                final boolean last = i == addresses.length - 1;
    
                Socket sock = sf.createSocket(context);
                sock.setSoTimeout(socketConfig.getSoTimeout());
                sock.setReuseAddress(socketConfig.isSoReuseAddress());
                sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
                sock.setKeepAlive(socketConfig.isSoKeepAlive());
                if (socketConfig.getRcvBufSize() > 0) {
                    sock.setReceiveBufferSize(socketConfig.getRcvBufSize());
                }
                if (socketConfig.getSndBufSize() > 0) {
                    sock.setSendBufferSize(socketConfig.getSndBufSize());
                }
    
                final int linger = socketConfig.getSoLinger();
                if (linger >= 0) {
                    sock.setSoLinger(true, linger);
                }
                conn.bind(sock);
    
                final InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
                if (this.log.isDebugEnabled()) {
                    this.log.debug("Connecting to " + remoteAddress);
                }
                try {
                    sock = sf.connectSocket(
                            connectTimeout, sock, host, remoteAddress, localAddress, context);
                    conn.bind(sock);
                    if (this.log.isDebugEnabled()) {
                        this.log.debug("Connection established " + conn);
                    }
                    return;
                } catch (final SocketTimeoutException ex) {
                    if (last) {
                        throw new ConnectTimeoutException(ex, host, addresses);
                    }
                } catch (final ConnectException ex) {
                    if (last) {
                        final String msg = ex.getMessage();
                        throw "Connection timed out".equals(msg)
                                        ? new ConnectTimeoutException(ex, host, addresses)
                                        : new HttpHostConnectException(ex, host, addresses);
                    }
                } catch (final NoRouteToHostException ex) {
                    if (last) {
                        throw ex;
                    }
                }
                if (this.log.isDebugEnabled()) {
                    this.log.debug("Connect to " + remoteAddress + " timed out. " +
                            "Connection will be retried using another IP address");
                }
            }
        }
    }    
    

    DefaultHttpClientConnectionOperator的connect先通过getSocketFactoryRegistry获取Lookup<ConnectionSocketFactory>,再通过它获取ConnectionSocketFactory,之后通过dnsResolver解析地址,再通过schemePortResolver解析port,然后通过ConnectionSocketFactory创建socket,并根据socketConfig设置socket的参数,最后执行connectSocket,并绑定到conn

    connectSocket

    org/apache/http/conn/socket/PlainConnectionSocketFactory.java

    public class PlainConnectionSocketFactory implements ConnectionSocketFactory {
    
        public static final PlainConnectionSocketFactory INSTANCE = new PlainConnectionSocketFactory();
    
        public static PlainConnectionSocketFactory getSocketFactory() {
            return INSTANCE;
        }
    
        public PlainConnectionSocketFactory() {
            super();
        }
    
        @Override
        public Socket createSocket(final HttpContext context) throws IOException {
            return new Socket();
        }
    
        @Override
        public Socket connectSocket(
                final int connectTimeout,
                final Socket socket,
                final HttpHost host,
                final InetSocketAddress remoteAddress,
                final InetSocketAddress localAddress,
                final HttpContext context) throws IOException {
            final Socket sock = socket != null ? socket : createSocket(context);
            if (localAddress != null) {
                sock.bind(localAddress);
            }
            try {
                sock.connect(remoteAddress, connectTimeout);
            } catch (final IOException ex) {
                try {
                    sock.close();
                } catch (final IOException ignore) {
                }
                throw ex;
            }
            return sock;
        }
    
    }
    

    PlainConnectionSocketFactory的createSocket直接new一个socket,其connectSocket方法则执行sock.connect

    socketConfig

    resolveSocketConfig

    org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java

        private SocketConfig resolveSocketConfig(final HttpHost host) {
            SocketConfig socketConfig = this.configData.getSocketConfig(host);
            if (socketConfig == null) {
                socketConfig = this.configData.getDefaultSocketConfig();
            }
            if (socketConfig == null) {
                socketConfig = SocketConfig.DEFAULT;
            }
            return socketConfig;
        }
    

    PoolingHttpClientConnectionManager的resolveSocketConfig先是从configData根据指定host获取socketConfig,若为null则再从configData获取默认的socketConfig,若为null则返回默认的socketConfig

    setSocketConfig

    org/apache/http/impl/conn/PoolingHttpClientConnectionManager.java

        public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) {
            this.configData.setDefaultSocketConfig(defaultSocketConfig);
        }
    
        public void setSocketConfig(final HttpHost host, final SocketConfig socketConfig) {
            this.configData.setSocketConfig(host, socketConfig);
        }
    

    PoolingHttpClientConnectionManager提供了setDefaultSocketConfig、setSocketConfig方法

    SocketConfig.DEFAULT

    org/apache/http/config/SocketConfig.java

    public class SocketConfig implements Cloneable {
    
        public static final SocketConfig DEFAULT = new Builder().build();
    
        //......
    
        public static class Builder {
    
            private int soTimeout;
            private boolean soReuseAddress;
            private int soLinger;
            private boolean soKeepAlive;
            private boolean tcpNoDelay;
            private int sndBufSize;
            private int rcvBufSize;
            private int backlogSize;
    
            Builder() {
                this.soLinger = -1;
                this.tcpNoDelay = true;
            }
    
            //......
        }
    }        
    

    默认的socketConfig,除了tcpNoDelay为true,其他的都为false,然后soLinger为-1

    小结

    HttpClientConnectionOperator定义了connect及upgrade方法,它有一个默认的实现类为DefaultHttpClientConnectionOperator;DefaultHttpClientConnectionOperator的connect先通过getSocketFactoryRegistry获取Lookup<ConnectionSocketFactory>,再通过它获取ConnectionSocketFactory,之后通过dnsResolver解析地址,再通过schemePortResolver解析port,然后通过ConnectionSocketFactory创建socket,并根据socketConfig设置socket的参数,最后执行connectSocket,并绑定到conn;默认的socketConfig,除了tcpNoDelay为true,其他的都为false,然后soLinger为-1。

    相关文章

      网友评论

          本文标题:聊聊httpclient的connect

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