Httpclient4.5详解之源码解析和使用一: 关键执行流程源码解读
承接上文,本文我们查看httpcommons连接池的源码来分析一下连接池将连接缓存起来然后借出的归还的,其中建立的socket连接是如何关闭的,现在我们就通过源码来一看究竟。
创建和借出
上文源码分析流程提到,具体流程会执行到InternalHttpClient#doExecute和execChain#execute(其调用链最后一站为MainClientExec), MainClientExec#execute方法上文我们已经分析过他通过连接发送请求的代码,但是连接如何获取的我们还没有分析,现在再次进入到他的代码中查看。
final ConnectionRequest connRequest = connManager.requestConnection(route, userToken);
if (execAware != null) {
if (execAware.isAborted()) {
connRequest.cancel();
throw new RequestAbortedException("Request aborted");
} else {
execAware.setCancellable(connRequest);
}
}
final RequestConfig config = context.getRequestConfig();
final HttpClientConnection managedConn;
try {
final int timeout = config.getConnectionRequestTimeout();
managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
} catch(final InterruptedException interrupted) {
Thread.currentThread().interrupt();
throw new RequestAbortedException("Request aborted", interrupted);
} catch(final ExecutionException ex) {
Throwable cause = ex.getCause();
if (cause == null) {
cause = ex;
}
throw new RequestAbortedException("Request execution failed", cause);
}
其中第1行调用PoolingHttpClientConnectionManager#requestConnection返回一个ConnectionRequest,进入其方法内部查看
public ConnectionRequest requestConnection(
final HttpRoute route,
final Object state) {
Args.notNull(route, "HTTP route");
if (this.log.isDebugEnabled()) {
this.log.debug("Connection request: " + format(route, state) + formatStats(route));
}
final Future<CPoolEntry> future = this.pool.lease(route, state, null);
return new ConnectionRequest() {
@Override
public boolean cancel() {
return future.cancel(true);
}
@Override
public HttpClientConnection get(
final long timeout,
final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException {
final HttpClientConnection conn = leaseConnection(future, timeout, tunit);
if (conn.isOpen()) {
final HttpHost host;
if (route.getProxyHost() != null) {
host = route.getProxyHost();
} else {
host = route.getTargetHost();
}
final SocketConfig socketConfig = resolveSocketConfig(host);
conn.setSocketTimeout(socketConfig.getSoTimeout());
}
return conn;
}
};
}
其中返回了一个有一个get方法的ConnectionRequest匿名内部类,所以才会有上个代码片段的第16行代码 managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS); 其中get方法中有final HttpClientConnection conn = leaseConnection(future, timeout, tunit)这段代码就是通过future这个参数获取connection的,在leaseConnection方法中通过entry = future.get(timeout, tunit)获取到已经包装好的连接对象CPoolEntry,所以现在重点就到了这个future身上,而这个future获取是在第8行。进入到Cpool的父类AbstractConnPool#lease具体看他的get方法
public E get(final long timeout, final TimeUnit tunit) throws InterruptedException, ExecutionException, TimeoutException {
final E entry = entr