上面介绍了HttpClientConnection是最核心的接口
这个接口的使用分布在上图所示的类或者接口中。图中可以看出,分成四个大类:Connection的不同实现,Manager,Operator,Factory。
由于这里不着重考察Connection的实现,所以略过,而考察Manager,Operator,Factory。
经过简单的代码筛查,可以发现在管理HttpClientConnection过程中,它以一个子接口:ManagedHttpClientConnection的形式存在。
这个很容易理解,HttpClientConnection的实例并不是用户new出来的,而是经由Manager管理的,所以衍生出一个Managed的接口。
此外,Operator和Factory也是经由Manager来调用的,所以可以先从 HttpClientConnectionManager 来切入代码。
接口 HttpClientConnectionManager 有两个实现
显然,BasicHttpClientConnectionManager 是最基本的可用版本。那么我们来看一下其中的代码。
从其中的参数可以看到,这里面有一个 Connection和一个Route,也就是说这个基础版的Manager只能管理一个Connection。
|
来看最核心的方法:getConnection, connect
synchronized HttpClientConnection getConnection(final HttpRoute route, final Object state) {
Asserts.check(!this.isShutdown.get(), "Connection manager has been shut down");
if (this.log.isDebugEnabled()) {
this.log.debug("Get connection for route " + route);
}
Asserts.check(!this.leased, "Connection is still allocated");
if (!LangUtils.equals(this.route, route) || !LangUtils.equals(this.state, state)) {
closeConnection();
}
this.route = route; //创建链接的时候目标主机的信息是一定要的
this.state = state;
checkExpiry(); //校验是否存在
if (this.conn == null) {
this.conn = this.connFactory.create(route, this.connConfig); // 这个Manager管理唯一一个Connection,如果获取连接的时候不存在则新建一个。
}
this.leased = true;
return this.conn;
}
@Override
public void connect( final HttpClientConnection conn, final HttpRoute route, final int connectTimeout, final HttpContext context ) throws IOException {
Args.notNull(conn, "Connection");
Args.notNull(route, "HTTP route");
Asserts.check(conn == this.conn, "Connection not obtained from this manager");
final HttpHost host;
if (route.getProxyHost() != null) {
host = route.getProxyHost();
} else {
host = route.getTargetHost();
}
final InetSocketAddress localAddress = route.getLocalSocketAddress();
this.connectionOperator.connect(this.conn, host, localAddress,
connectTimeout, this.socketConfig, context); // 最终还是依赖Operator来连接, context是外面传进来的
}
|
从代码可以看出来,Connection的生成管理连接释放都是通过Manager来完成的,这样就让Connection只需要维持核心逻辑即:发送Response和接受Response(参考HttpClientConnection接口)。
但是,最核心的连接过程最终还是通过ConnectionOperator来完成的,下面来看HttpClientConnecitonOperator,这个接口就只有一个实现,即DefaultHttpClientConnectionOperator
这个类很简单的,先看看最核心的 connect方法。
@Override
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()); // 从Context中获取SocketFactory,可以看到,根据Scheme(是否是Https)不一样会有不同。这里看到Context是做什么用的。
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); // connect 的过程,也就是新建Socket并绑定到HttpClientConnection上的过程。
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();
if ("Connection timed out".equals(msg)) {
throw new ConnectTimeoutException(ex, host, addresses);
} else {
throw 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");
}
}
}
|
ConnectionSocketFactory也只有两个实现:PlainConnectionSocketFactory,SSLConnectionSocketFactory(实现了LayeredConnectionSocketFactory,继承自ConnectionSocketFactory)。
PlainConnectionSocketFactory是用于创建非HTTPS连接的,代码很简单,本质上就是 new Socket(); (关于Socket编程,是Java基础内容,不解释)
而SSLConnectionSocketFactory就是在创建Socket前后有很多SSL相关的操作,这里不详细说明了。
|
通过代码可以看到, ConnectionOperator 本身只做一件事情,就是处理Connection和Socket的关系,这也是Connection管理的一部分,简单点来说直接集成到Manager中也是可以的。为什么要单独抽离这一部分,按照我的理解,由于这个关系中可能出现 Http 和 HTTPs 的不同处理,不是所有人都有编写的能力。常规的业务代码,开发团队设计好就可以了,但是对于应对规范的(如HTTPS规范),则需要特定的领域专家来完成。
上面两个部分:Manager和Operator是对现成的HttpClientConnection进行管理,下面来看HttpClientConnection怎么生成的。核心的接口就是HttpConnectionFactory。
其实现就两个,DefaultBHttpClientConnectionFactory 和 ManagedHttpClientConnectionFactory。实现上都差不多,前者是默认的,后者是生成 ManageredHttpClientConnection。由于我们在这里是通过Manager来管理的,所以重点看 ManageredHttpClientConnection,Default那个差不多的
@Override
public ManagedHttpClientConnection create(final HttpRoute route, final ConnectionConfig config) {
final ConnectionConfig cconfig = config != null ? config : ConnectionConfig.DEFAULT;
CharsetDecoder chardecoder = null;
CharsetEncoder charencoder = null;
final Charset charset = cconfig.getCharset();
final CodingErrorAction malformedInputAction = cconfig.getMalformedInputAction() != null ?
cconfig.getMalformedInputAction() : CodingErrorAction.REPORT;
final CodingErrorAction unmappableInputAction = cconfig.getUnmappableInputAction() != null ?
cconfig.getUnmappableInputAction() : CodingErrorAction.REPORT;
if (charset != null) {
chardecoder = charset.newDecoder();
chardecoder.onMalformedInput(malformedInputAction);
chardecoder.onUnmappableCharacter(unmappableInputAction);
charencoder = charset.newEncoder();
charencoder.onMalformedInput(malformedInputAction);
charencoder.onUnmappableCharacter(unmappableInputAction);
}
final String id = "http-outgoing-" + Long.toString(COUNTER.getAndIncrement());
return new LoggingManagedHttpClientConnection(
id,
log,
headerlog,
wirelog,
cconfig.getBufferSize(),
cconfig.getFragmentSizeHint(),
chardecoder,
charencoder,
cconfig.getMessageConstraints(),
incomingContentStrategy,
outgoingContentStrategy,
requestWriterFactory,
responseParserFactory);
}
|
LoggingManagedHttpClientConnection extends DefaultManagedHttpClientConnection extendsDefaultBHttpClientConnection
这在前面讲过了,也就是核心的,HttpClientConnection 怎样利用 Socket 来发送Request以及接收Response。
|
到这里,就吧BasicHttpClientManager的核心代码看完了。对于PoolingHttpClientConnectionManager, 涉及到对象池化的概念。、
在常用的Apache Commons Pool 中可以是这样定义的:
PoolableObjectFactory用于管理被池化的对象的产生、**、挂起、校验和销毁;
ObjectPool用于管理要被池化的对象的借出和归还,并通知PoolableObjectFactory完成相应的工作;
我们来看看PoolingHttpClientConnectionManager是如何来定义对象池的。正常的Pool管理中有一个Factory,从代码中可以看出来,是InternalConnectionFactory。同时还有一个ObjectPool,在这里是CPool,但是它基本是个空的封装,继承自AbstractConnPool。 其实PoolingHttpClientConnectionManager看上去结构简单,也能做很多事情,但是最终还是落到了CPool上。而CPool又落到了AbstractConnPool上。
AbstractConnPool的代码详细分析,暂时不讲了,理解核心的逻辑就可以了。
图中还有一个重要的接口没有表达出来:ManagedHttpClientConnection。跟图中很多类有关联关系,因为它是最终生成的结果。所以如果加到图上,那这个图就成了一个网状结构了。
只需要知道,这个接口贯穿整个过程就可以了。