跟踪RestTemplate源码,分析HTTP线程池实现原理

        RestTemplate是Spring5默认的http请求工具,从Spring6开始默认http请求工具变成了WebClient。

        首先,在RestTemplate调用处打上断点

        这里以postForEntity方法为入口跟踪。F7进入

         一眼望去,execute肯定是关键代码,继续跟进

        do开头的一定是核心逻辑(Spring都是这个风格)

        这个方法里,当调用request.execute()后就获得了response,说明调用的具体方法在execute()方法里。其中createRequest方法是创建http调用的客户端方法,RestTemplate只是一个壳,也就是Spring封装的一个工具而已,底层实现还是以第三方的为主,例如:HttpClient、OKHttp等,本项目中使用的事HttpClient。具体创建过程这里略过,贴一下创建的代码。如下:

public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
    HttpClient client = getHttpClient();

    HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
    postProcessHttpRequest(httpRequest);
    HttpContext context = createHttpContext(httpMethod, uri);
    if (context == null) {
       context = HttpClientContext.create();
    }

    // Request configuration not set in the context
    if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
       // Use request configuration given by the user, when available
       RequestConfig config = null;
       if (httpRequest instanceof Configurable) {
          config = ((Configurable) httpRequest).getConfig();
       }
       if (config == null) {
          config = createRequestConfig(client);
       }
       if (config != null) {
          context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
       }
    }

    if (this.bufferRequestBody) {
       return new HttpComponentsClientHttpRequest(client, httpRequest, context);
    }
    else {
       return new HttpComponentsStreamingClientHttpRequest(client, httpRequest, context);
    }
}

另外,requestCallback.doWithRequest方法是完善请求内容的,例如请求头处理,最重要的事设置消息处理器。代码如下:

public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
    super.doWithRequest(httpRequest);
    Object requestBody = this.requestEntity.getBody();
    if (requestBody == null) {
       HttpHeaders httpHeaders = httpRequest.getHeaders();
       HttpHeaders requestHeaders = this.requestEntity.getHeaders();
       if (!requestHeaders.isEmpty()) {
          requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
       }
       if (httpHeaders.getContentLength() < 0) {
          httpHeaders.setContentLength(0L);
       }
    }
    else {
       Class<?> requestBodyClass = requestBody.getClass();
       Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
             ((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
       HttpHeaders httpHeaders = httpRequest.getHeaders();
       HttpHeaders requestHeaders = this.requestEntity.getHeaders();
       MediaType requestContentType = requestHeaders.getContentType();
       for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
          if (messageConverter instanceof GenericHttpMessageConverter) {
             GenericHttpMessageConverter<Object> genericConverter =
                   (GenericHttpMessageConverter<Object>) messageConverter;
             if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
                if (!requestHeaders.isEmpty()) {
                   requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
                }
                if (logger.isDebugEnabled()) {
                   if (requestContentType != null) {
                      logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
                            "\" using [" + messageConverter + "]");
                   }
                   else {
                      logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
                   }

                }
                genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest);
                return;
             }
          }
          else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
             if (!requestHeaders.isEmpty()) {
                requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values)));
             }
             if (logger.isDebugEnabled()) {
                if (requestContentType != null) {
                   logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
                         "\" using [" + messageConverter + "]");
                }
                else {
                   logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
                }

             }
             ((HttpMessageConverter<Object>) messageConverter).write(
                   requestBody, requestContentType, httpRequest);
             return;
          }
       }
       String message = "Could not write request: no suitable HttpMessageConverter found for request type [" +
             requestBodyClass.getName() + "]";
       if (requestContentType != null) {
          message += " and content type [" + requestContentType + "]";
       }
       throw new RestClientException(message);
    }
}

        回到request.execute()方法中:

此时入参已经被转换成了字节,

又看到了熟悉的do开头的方法

        这时方法已经进入到了httpClient包内了,说明开始调用第三方框架了。继续跟进这个方法

        看到了pool终于见到了连接池,

pool里有很多属性,比如:maxTotal  、defaultMaxPerRoute等这是项目配置的参数。注意看这里有个routeToPool,这个路径和连接池的对照。也就是你要放某个ip地址,这个地址会生成一个连接池,他们是共享maxTotal这个参数,这里http连接池和数据库连接池还是有区别。数据库连接池是有初始化连接的,但是这里没有,因为http请求在初始化时并不能完全知道要调用哪个地址也就无法完成初始化,所以这里只能根据具体的请求来初始化routeToPool这个连接池,在http连接池里你是看不到初始连接这个参数的。leased、available、pending这几个是连接池的核心参数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hamilton_Huan

原创不易,结合业务原创更不易

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值