序
本文主要研究一下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();}@Overridepublic Socket createSocket(final HttpContext context) throws IOException {return new Socket();}@Overridepublic 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。