问题

项目中有个远程服务因为某些原因会访问不通,于是就在调用的那一步挂起无法结束了。

查看代码

代码大概如下

CloseableHttpClient closeableHttpClient = HttpClients.custom()                .setConnectionManager(manager)                .build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();requestFactory.setHttpClient(httpClient);clientHttpRequestFactory.setConnectTimeout(1000);RestTemplate restTemplate = new RestTemplate();restTemplate.setRequestFactory(requestFactory);

分析

HttpComponentsClientHttpRequestFactory底层默认使用了apache的HttpClient,超时设置就针对其设置的。其实我们只需要设置ReadTimeout就好了。

CloseableHttpClient closeableHttpClient = HttpClients.custom()
.setConnectionManager(manager)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
clientHttpRequestFactory.setConnectTimeout(1000);
clientHttpRequestFactory.setReadTimeout(50);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(requestFactory);

但我们发现,我们并没有使用默认的HttpClient,而是显式的指定了Client,我们在创建Client的时候也可以指定其的各种超时属性:

RequestConfig defaultRequestConfig = RequestConfig.custom()
.setConnectTimeout(2 * 1000)
.setSocketTimeout(5 * 1000)
.setConnectionRequestTimeout(2 * 1000)
.build(); CloseableHttpClient closeableHttpClient = HttpClients.custom()
.setDefaultRequestConfig(getRequestConfig())
.setConnectionManager(manager)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
clientHttpRequestFactory.setConnectTimeout(1000);
clientHttpRequestFactory.setReadTimeout(50);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(requestFactory);

上面可以看到,我们在创建Client的时候指定了超时时间,也在创建HttpComponentsClientHttpRequestFactory的时候指定了超时时间,那么到底以哪个为准呢?

一探究竟

项目中使用的是spring web 4.3.5, 我们看看源代码的实现,以下是HttpComponentsClientHttpRequestFactory类的部分方法:

https://github.com/spring-projects/spring-framework/blob/4.3.x/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.java

/**
* Set the socket read timeout for the underlying HttpClient.
* A timeout value of 0 specifies an infinite timeout.
* <p>Additional properties can be configured by specifying a
* {@link RequestConfig} instance on a custom {@link HttpClient}.
* @param timeout the timeout value in milliseconds
* @see RequestConfig#getSocketTimeout()
*/
public void setReadTimeout(int timeout) {
Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value");
this.requestConfig = requestConfigBuilder().setSocketTimeout(timeout).build();
setLegacySocketTimeout(getHttpClient(), timeout);
}
@Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
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(getHttpClient());
}
if (config != null) {
context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
}
} if (this.bufferRequestBody) {
return new HttpComponentsClientHttpRequest(getHttpClient(), httpRequest, context);
}
else {
return new HttpComponentsStreamingClientHttpRequest(getHttpClient(), httpRequest, context);
}
}
    /**
* Create a default {@link RequestConfig} to use with the given client.
* Can return {@code null} to indicate that no custom request config should
* be set and the defaults of the {@link HttpClient} should be used.
* <p>The default implementation tries to merge the defaults of the client
* with the local customizations of this factory instance, if any.
* @param client the {@link HttpClient} (or {@code HttpAsyncClient}) to check
* @return the actual RequestConfig to use (may be {@code null})
* @since 4.2
* @see #mergeRequestConfig(RequestConfig)
*/
protected RequestConfig createRequestConfig(Object client) {
if (client instanceof Configurable) {
RequestConfig clientRequestConfig = ((Configurable) client).getConfig();
return mergeRequestConfig(clientRequestConfig);
}
return this.requestConfig;
}
    /**
* Merge the given {@link HttpClient}-level {@link RequestConfig} with
* the factory-level {@link RequestConfig}, if necessary.
* @param clientConfig the config held by the current
* @return the merged request config
* @since 4.2
*/
protected RequestConfig mergeRequestConfig(RequestConfig clientConfig) {
if (this.requestConfig == null) { // nothing to merge
return clientConfig;
} RequestConfig.Builder builder = RequestConfig.copy(clientConfig);
int connectTimeout = this.requestConfig.getConnectTimeout();
if (connectTimeout >= 0) {
builder.setConnectTimeout(connectTimeout);
}
int connectionRequestTimeout = this.requestConfig.getConnectionRequestTimeout();
if (connectionRequestTimeout >= 0) {
builder.setConnectionRequestTimeout(connectionRequestTimeout);
}
int socketTimeout = this.requestConfig.getSocketTimeout();
if (socketTimeout >= 0) {
builder.setSocketTimeout(socketTimeout);
}
return builder.build();
}

我们可以看到,版本4.2之后,如果二者都进行了参数指定,会有一个mergeRequestConfig的操作,超时以HttpComponentsClientHttpRequestFactory类指定为为准。

结论

所以,针对本文一开始的问题,我们只需要设置HttpComponentsClientHttpRequestFactory实例的setReadTimeout方法即可。

参考

https://leokongwq.github.io/2018/11/21/springboot-resttempate-timout.html

https://www.cnblogs.com/softidea/p/6964347.html

但我们发现,我们显式的指定了Client,我们在创建Client的时候可以指定其的各种超时属性:

Spring web之restTemplate超时问题处理的更多相关文章

  1. Spring Cloud ZooKeeper集成Feign的坑1,错误:Consider defining a bean of type 'org.springframework.web.client.RestTemplate' in your configuration.

    错误如下: ERROR 31473 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** A ...

  2. spring web.xml 难点配置总结

    web.xml web.xml是所有web项目的根源,没有它,任何web项目都启动不了,所以有必要了解相关的配置. ContextLoderListener,ContextLoaderServlet, ...

  3. Spring Boot使用RestTemplate消费REST服务的几个问题记录

    我们可以通过Spring Boot快速开发REST接口,同时也可能需要在实现接口的过程中,通过Spring Boot调用内外部REST接口完成业务逻辑. 在Spring Boot中,调用REST Ap ...

  4. spring web.xml 难点配置总结【转】

    web.xml web.xml是所有web项目的根源,没有它,任何web项目都启动不了,所以有必要了解相关的配置. ContextLoderListener,ContextLoaderServlet, ...

  5. 001-快速搭建Spring web应用【springboot 2.0.4】-gradle、springboot的启动过程分析、gradle多模块构建

    一.概述 学习<精通Spring MVC4>书籍笔记 二.笔记 1.快速构建Spring starter web项目几种方式 1>使用Spring Tool Suite生成Start ...

  6. spring boot 注入 restTemplate

    转载自:http://blog.csdn.net/liuchuanhong1/article/details/54631080 package com.chhliu.springboot.restfu ...

  7. Spring WebClient vs. RestTemplate

    1. 简介 本教程中,我们将对比 Spring 的两种 Web 客户端实现 -- RestTemplate 和 Spring 5 中全新的 Reactive 替代方案 WebClient. 2. 阻塞 ...

  8. 使用 Spring 提供的 restTemplate 完成 Http 服务消费

    RestTemplate 介绍 RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,能够大大提高 ...

  9. Consider defining a bean of type 'org.springframework.web.client.RestTemplate' in your configuration

    https://www.cnblogs.com/EasonJim/p/7546136.html 错误如下: ERROR 31473 --- [ main] o.s.b.d.LoggingFailure ...

随机推荐

  1. 5.1中repair table

    mysql> repair table xs;+---------+--------+----------+----------+| Table | Op | Msg_type | Msg_te ...

  2. VB基础总结

    前段时间用VB写了一个简单窗口小应用,久了不碰VB,都忘了,下面用思维导图简单总结了一些基础的东西,方便以后快速查阅.

  3. oracle视图添加hint

    /* Formatted on 2019/8/6 下午 02:51:21 (QP5 v5.163.1008.3004) */ SELECT DB FROM ( SELECT /*+ index(A.r ...

  4. FI_F4_ZTERM付款条件代码

    这个函数可以弹出一个选择帮助,返回一个付款条件代码 CALL FUNCTION 'FI_F4_ZTERM' EXPORTING I_KOART = 'K' " K为供应商,D为客户 * I_ ...

  5. 全网最详细的PyCharm+Anaconda的安装。

    目录 PyCharm的安装 一.下载安装包 1.安装网站 2.在导航栏输入网址回车 3.点击 DOWNLOAD. 4.它有专业版和社区版,我们下载社区版就可以使用了.(专业版要收费) 二.安装过程 5 ...

  6. CentOS 7.4通过rpm包离线安装 Mysql8.0并部署主从复制(附从库备份脚本)

    一. 部署MySQL (两个节点都做) 下载 rpm包 wget https://goodrain-pkg.oss-cn-shanghai.aliyuncs.com/mysql8.rpm tar xv ...

  7. 前端工程构建之谈:gulp3要不要升级到Gulp4

    关于升级还是不升级,这是一个哲学问题. gulp4的语法更加现代,支持ES6的大部分写法,使用exports的方式去暴露任务组合,更加灵活和便捷. gulp4同时也提供了很多强大的API,例如para ...

  8. mysql(视图 事务 索引 外键)

    视图   视图本质就是对查询的封装   创建视图(定义视图 起名以v_开头) create view v_students as select classes.name as c_name ,stud ...

  9. Soul 网关 Nacos 数据同步源码解析

    学习目标: 学习Soul 网关 Nacos 数据同步源码解析 学习内容: 环境配置 Soul 网关 Nacos 数据同步基本概念 源码分析 学习时间:2020年1月28号 早7点 学习产出: 环境配置 ...

  10. Java Optional使用指南

    提到NullPointerException(简称NPE)异常,相信每个Java开发人员都不陌生,从接触编程的第1天起,它就和我们如影随形,最近处理的线上bug中,有不少都是对象没判空导致的NullP ...