目录

HttpClient 4.5.3 源码浅析( HttpClient 的 execute 方法执行过程)

目录

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

http://peierlong-blog.oss-cn-hongkong.aliyuncs.com/Xnip2018-06-180_15-08-22.jpg

根据上一篇文章我们知道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);
      }
  }

以上代码中

  1. HttpExecutionAware类是用于接收阻塞I/O操作的通知。

  2. HttpRequestWrapper类是HttpRequest接口的包装类。

  3. HttpClientContext类实现HttpContext接口,表示HTTP进程的执行状态。

  4. RequestConfig请求配置类,初始化后赋值到了上下文HttpClientContext中。

  5. HttpRoute为路由类。想更深了解可以参考这篇文章


接下来主要逻辑在MainClientExecexecute()方法,涉及到有关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得到一个HttpClientConnectionHttpClientConnection 可用于通过指定路由路径进行通信。

 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();
              }
          }
      }
  1. managedConn放入context上下文中

  2. 对失效链接进行校验

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()判断中,暂不做分析。我们来对以上代码进行一个简要的分析:

  1. 判断在第二次循环以后是否可以重复读,如果不可以重复读,则抛出异常。(不可重复读的指的是流,像StringEntity FileEntity这样的都是可以重复读的)

  2. 好几处地方都对execAware真正的request的是否终止isAborted()方法做校验,如果终止了,则抛出异常。有了这个我们就可以随时取消请求了。

  3. 如果链接不是open状态的,那么使用route路由对象重新建立一次链接。

  4. 对链接进行超时设置。

  5. 使用请求执行器requestExecutor执行请求得到response对象。

  6. 根据消息头判断链接是否要保持长链接,如果是,标记connHolder为可重用的,如果否,则标记为不可重用的。

注:在执行请求的时候,用到的requestHttpRequestWrapper包装类,使用包装类是防止真正的请求操作时发生改变。

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);
      }
  1. 从返回的respnse中得到HttpEntity,校验实体如果是空或者不是一个流,则释放链接。

  2. 使用HttpResponseProxy代理类对response进行代理,如果读取完了响应,那么这个响应就会关闭。


至此,HttpClient对象的execute()方法的简要的执行流程基本上就分析完毕。