参考自:http://events.jianshu.io/p/477e7a3179c6
大家都知道在SpringBoot中一般适用RestTemplate来进行远程调用,那么SpringBoot中如何默认配置RestTemplate,以及如何自定义配置自己的RestTemplate,RestTemplate异步请求如何实现等
可以通过使用ClientHttpRequestFactory指定不同的HTTP请求方式
ClientHttpRequestFactory接口主要提供了三种实现方式
优点:连接池、超时时间设置、支持异步、请求和响应的编解码
缺点:依赖别的spring版块、参数传递不灵活
RestTemplate默认是使用SimpleClientHttpRequestFactory,内部是调用jdk的HttpConnection,默认超时为-1
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5.13version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
http:
maxTotal: 100 #最大连接数
defaultMaxPerRoute: 20 #并发数
connectTimeout: 1000 #创建连接的最长时间
connectionRequestTimeout: 500 #从连接池中获取到连接的最长时间
socketTimeout: 10000 #数据传输的最长时间
staleConnectionCheckEnabled: true #提交请求前测试连接是否可用
validateAfterInactivity: 3000000 #可用空闲连接过期时间,重用空闲连接时会先检查是否空闲时间超过这个时间,如果超过,释放socket重新建立
@Configuration
public class RestTemplateConfig {
/*@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}*/
@Value("${http.maxTotal}")
private Integer maxTotal;
@Value("${http.defaultMaxPerRoute}")
private Integer defaultMaxPerRoute;
@Value("${http.connectTimeout}")
private Integer connectTimeout;
@Value("${http.connectionRequestTimeout}")
private Integer connectionRequestTimeout;
@Value("${http.socketTimeout}")
private Integer socketTimeout;
@Value("${http.staleConnectionCheckEnabled}")
private boolean staleConnectionCheckEnabled;
@Value("${http.validateAfterInactivity}")
private Integer validateAfterInactivity;
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(httpRequestFactory());
}
@Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
@Bean
public HttpClient httpClient() {
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(maxTotal); // 最大连接数
connectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute); //单个路由最大连接数
connectionManager.setValidateAfterInactivity(validateAfterInactivity); // 最大空间时间
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(socketTimeout) //服务器返回数据(response)的时间,超过抛出read timeout
.setConnectTimeout(connectTimeout) //连接上服务器(握手成功)的时间,超出抛出connect timeout
.setStaleConnectionCheckEnabled(staleConnectionCheckEnabled) // 提交前检测是否可用
.setConnectionRequestTimeout(connectionRequestTimeout)//从连接池中获取连接的超时时间,超时间未拿到可用连接,会抛出org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool
.build();
//headers
List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36"));
headers.add(new BasicHeader("Accept-Encoding", "gzip,deflate"));
headers.add(new BasicHeader("Accept-Language", "zh-CN"));
headers.add(new BasicHeader("Connection", "Keep-Alive"));
headers.add(new BasicHeader("Content-type", "application/json;charset=UTF-8"));
return HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig)
.setConnectionManager(connectionManager)
.setDefaultHeaders(headers)
// 保持长连接配置,需要在头添加Keep-Alive
.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy())
//重试次数,默认是3次,没有开启
.setRetryHandler(new DefaultHttpRequestRetryHandler(2, true))
.build();
}
}
【一些配置解释】
懒得写了:http://events.jianshu.io/p/477e7a3179c6
见我的博客:https://blog.csdn.net/hc1285653662/article/details/127001439
参考:
https://blog.csdn.net/qq_38622452/article/details/81874483
https://blog.csdn.net/Carson073/article/details/108147005
AsyncRestTemplate是在Spring4.0中对RestTemplate进行扩展产生的新类,通过返回ListenableFuture对象生成回调机制,以达到异步非阻塞发送http请求
缺点:
@GetMapping("/sync/hello")
public String syncHello() {
AsyncRestTemplate template = new AsyncRestTemplate();
String url = "http://localhost:8083/sync/hello";//休眠5秒的服务
//调用完后立即返回(没有阻塞)
ListenableFuture<ResponseEntity<String>> forEntity = template.getForEntity(url, String.class);
//异步调用后的回调函数
forEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
//调用失败
@Override
public void onFailure(Throwable ex) {
log.error("远程调用faliure");
}
//调用成功
@Override
public void onSuccess(ResponseEntity<String> result) {
log.info("res---->"+result.getBody());
}
});
log.info("调用结束");
return "ok";
}
CompletableFuture 学习见我博客:
添加链接描述
@Component
@ConfigurationProperties(prefix = "my.thread")
@Data
public class ThreadPoolConfigProperties {
private Integer coreSize;
private Integer maxSize;
private Integer keepAliveTime;
}
/**
* @author houChen
* @date 2021/12/11 10:35
* @Description: 自定义线程池配置类
*/
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties properties){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
properties.getCoreSize(),
properties.getMaxSize(),
properties.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
return threadPoolExecutor;
}
}
2)通过CompletableFuture 发起异步请求
/**
* @Description: 通过CompletableFuture异步发起请求
* @author houChen
* @date 2022/9/24 20:18
*/
@GetMapping("/sync/hello1")
public String syncHello1() {
CompletableFuture.supplyAsync(() -> {
String url = "http://localhost:8083/sync/hello";//休眠5秒的服务
return restTemplate.getForEntity(url, String.class);
}, threadPoolExecutor)
.thenAccept(res -> {
log.info("res: {}", res.getBody());
}).exceptionally(e -> {
log.error("err:{}", e);
return null;
});
return "ok";
}
请求: http://localhost:8082/sync/hello1
@GetMapping("/sync/hello2")
public String syncHello2() {
helloWorldService.asyncRequest();
return "ok";
}
@Slf4j
@Service
public class HelloWorldService {
@Autowired
RestTemplate restTemplate;
// 指定使用自己定义的线程池中的线程就行调用
@Async("threadPoolExecutor")
public void asyncRequest() {
String url = "http://localhost:8083/sync/hello";//休眠5秒的服务
String res = restTemplate.getForEntity(url, String.class).getBody();
log.info("res :{}", res);
}
}