上一遍总结的HttpClient
对象的构造过程,本篇主要总结HttpClient
对象的execute()
方法的执行流程,我们先看整体的时序图(省略部分细节),然后一步步分析。当不知道流程的时候走debug也是不错的选择。

根据上一篇文章我们知道HttpClient
实例是使用的子类CloseableHttpClient
类初始化而来,所以我们看子类的execute()
方法的源代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/**
* {@inheritDoc}
*/
@Override
public CloseableHttpResponse execute(
final HttpUriRequest request) throws IOException, ClientProtocolException {
return execute(request, (HttpContext) null);
}
/**
* {@inheritDoc}
*/
@Override
public CloseableHttpResponse execute(
final HttpUriRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(request, "HTTP request");
return doExecute(determineTarget(request), request, context);
}
protected abstract CloseableHttpResponse doExecute(HttpHost target, HttpRequest request,
HttpContext context) throws IOException, ClientProtocolException;
|
我们看到调用链调用到了抽象方法doExecute()
,上一篇中,我们知道HttpClient
对象构建最后,是使用的CloseableHttpClient
的子类InternalHttpClient
创建的对象,所以我们来看InternalHttpClient
类的doExecute()
方法的具体实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
@Override
protected CloseableHttpResponse doExecute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(request, "HTTP request");
HttpExecutionAware execAware = null;
if (request instanceof HttpExecutionAware) {
execAware = (HttpExecutionAware) request;
}
try {
final HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request, target);
final HttpClientContext localcontext = HttpClientContext.adapt(
context != null ? context : new BasicHttpContext());
RequestConfig config = null;
if (request instanceof Configurable) {
config = ((Configurable) request).getConfig();
}
if (config == null) {
final HttpParams params = request.getParams();
if (params instanceof HttpParamsNames) {
if (!((HttpParamsNames) params).getNames().isEmpty()) {
config = HttpClientParamConfig.getRequestConfig(params, this.defaultConfig);
}
} else {
config = HttpClientParamConfig.getRequestConfig(params, this.defaultConfig);
}
}
if (config != null) {
localcontext.setRequestConfig(config);
}
setupContext(localcontext);
final HttpRoute route = determineRoute(target, wrapper, localcontext);
return this.execChain.execute(route, wrapper, localcontext, execAware);
} catch (final HttpException httpException) {
throw new ClientProtocolException(httpException);
}
}
|
以上代码中
-
HttpExecutionAware
类是用于接收阻塞I/O操作的通知。
-
HttpRequestWrapper
类是HttpRequest
接口的包装类。
-
HttpClientContext
类实现HttpContext
接口,表示HTTP进程的执行状态。
-
RequestConfig
请求配置类,初始化后赋值到了上下文HttpClientContext
中。
-
HttpRoute
为路由类。想更深了解可以参考这篇文章。
接下来主要逻辑在MainClientExec
的execute()
方法,涉及到有关https的auth的内容暂时不做分析。
1
2
3
|
if (request instanceof HttpEntityEnclosingRequest) {
RequestEntityProxy.enhance((HttpEntityEnclosingRequest) request);
}
|
如果是HttpEntityEnclosingRequest
类型的requet,则使用Entity代理类进行加强,主要用途是对entity的回收等操作。
1
|
Object userToken = context.getUserToken();
|
这是HttpClient
标识,默认为null,为了保证用户使用链接的唯一性。
1
|
final ConnectionRequest connRequest = connManager.requestConnection(route, userToken);
|
得到一个由connManager
管理其生命周期的ConnectionRequest
1
2
3
4
5
6
7
8
|
if (execAware != null) {
if (execAware.isAborted()) {
connRequest.cancel();
throw new RequestAbortedException("Request aborted");
} else {
execAware.setCancellable(connRequest);
}
}
|
execAware
也实际表示一个封装请求。把当前链接赋值给execAware
持有。
1
|
final RequestConfig config = context.getRequestConfig();
|
从context
中取出请求配置类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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);
}
|
通过connRequest
得到一个HttpClientConnection
,HttpClientConnection
可用于通过指定路由路径进行通信。
1
2
3
4
5
6
7
8
9
10
11
12
|
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn);
if (config.isStaleConnectionCheckEnabled()) {
// validate connection
if (managedConn.isOpen()) {
this.log.debug("Stale connection check");
if (managedConn.isStale()) {
this.log.debug("Stale connection detected");
managedConn.close();
}
}
}
|
-
把managedConn
放入context
上下文中
-
对失效链接进行校验
1
|
final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn);
|
创建一个链接持有者。
1
2
3
|
if (execAware != null) {
execAware.setCancellable(connHolder);
}
|
使execAware
请求request
拥有链接持有者,这样用户可以对request
可以直接对持有者进行操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
HttpResponse response;
for (int execCount = 1;; execCount++) {
if (execCount > 1 && !RequestEntityProxy.isRepeatable(request)) {
throw new NonRepeatableRequestException("Cannot retry request " +
"with a non-repeatable request entity.");
}
if (execAware != null && execAware.isAborted()) {
throw new RequestAbortedException("Request aborted");
}
if (!managedConn.isOpen()) {
this.log.debug("Opening connection " + route);
try {
establishRoute(proxyAuthState, managedConn, route, request, context);
} catch (final TunnelRefusedException ex) {
if (this.log.isDebugEnabled()) {
this.log.debug(ex.getMessage());
}
response = ex.getResponse();
break;
}
}
final int timeout = config.getSocketTimeout();
if (timeout >= 0) {
managedConn.setSocketTimeout(timeout);
}
if (execAware != null && execAware.isAborted()) {
throw new RequestAbortedException("Request aborted");
}
if (this.log.isDebugEnabled()) {
this.log.debug("Executing request " + request.getRequestLine());
}
// 。。。 省略auth相关
response = requestExecutor.execute(request, managedConn, context);
// 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();
}
// 。。。 省略auth相关、
}
|
这个代码块放在一个循环里边,循环的正常流程的终止条件在needAuthentication()
判断中,暂不做分析。我们来对以上代码进行一个简要的分析:
-
判断在第二次循环以后是否可以重复读,如果不可以重复读,则抛出异常。(不可重复读的指的是流,像StringEntity
FileEntity
这样的都是可以重复读的)
-
好几处地方都对execAware
真正的request
的是否终止isAborted()
方法做校验,如果终止了,则抛出异常。有了这个我们就可以随时取消请求了。
-
如果链接不是open状态的,那么使用route
路由对象重新建立一次链接。
-
对链接进行超时设置。
-
使用请求执行器requestExecutor
执行请求得到response
对象。
-
根据消息头判断链接是否要保持长链接,如果是,标记connHolder
为可重用的,如果否,则标记为不可重用的。
注:在执行请求的时候,用到的request
是HttpRequestWrapper
包装类,使用包装类是防止真正的请求操作时发生改变。
1
2
3
4
5
6
7
|
if (userToken == null) {
userToken = userTokenHandler.getUserToken(context);
context.setAttribute(HttpClientContext.USER_TOKEN, userToken);
}
if (userToken != null) {
connHolder.setState(userToken);
}
|
把当前用户标识保存给链接持有者。
1
2
3
4
5
6
7
8
9
|
// 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);
} else {
return new HttpResponseProxy(response, connHolder);
}
|
-
从返回的respnse
中得到HttpEntity
,校验实体如果是空或者不是一个流,则释放链接。
-
使用HttpResponseProxy
代理类对response进行代理,如果读取完了响应,那么这个响应就会关闭。
至此,HttpClient
对象的execute()
方法的简要的执行流程基本上就分析完毕。