Web客户端主要有2种,一种是RestTemplate,另一种是Reactor方式的WebClient,这里我们就RestTemplate方式进行梳理
基本使用
注入
redisTemplate等不同,SpringBoot没有直接提供RedisTemplate的直接注入使用,但它却提供了RestTemplateAutoConfiguatrion,也提供了RestTemplateBuilder,用于生成RestTemplate。这样注入就需要自己通过bean的方式注入。
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
ps:在仅使用RestTemplate而不适用web容器的情况下,可以通过WebApplicationType.NONE进行配置
基本接口
函数 | 说明 | |
getForEntity | 返回的ResponseEntity包含了响应体所映射成的对象 | |
getForObject | 返回的请求体将映射为一个对象 | |
postForEntity | POST 数据,返回包含一个对象的ResponseEntity | |
postForObject | POST 数据,返回根据响应体匹配形成的对象 | |
put | PUT 资源到特定的URL | |
delete | 在特定的URL上对资源执行HTTP DELETE操作 | |
exchange | 在URL上执行特定的HTTP方法,返回包含对象的ResponseEntity | |
// getForEntity
ResponseEntity<User> response = restTemplate.getForEntity("http://localhost/get/{id}", User.class, id);
User user = response.getBody();
// getForObject
User user = restTemplate.getForObject("http://localhost/get/{id}", User.class, id);
// postForEntity
ResponseEntity<String> response = restTemplate.postForEntity("http://localhost/save", user, String.class);
String body = response.getBody();
// postForObject
String body = restTemplate.postForObject("http://localhost/save", user, String.class);
URI构造
一般采用UriComponentsBuilder进行构建
URI uri = UriComponentsBuilder
.fromUriString("http://example.com/hotels/{hotel}?id={id}")
.build("Westin", "1");
ResponseEntity<Coffee> c = restTemplate.getForEntity(uri, Hotel.class);
在一些测试环境下,可以采用MvcUriComponentsBuilder
UriComponents uriComponents = MvcUriComponentsBuilder.fromMethodCall(
on(AddressController.class).getAddressesForCountry("US")).buildAndExpand(1);
URI uri = uriComponents.encode().toUri()
高级使用
如果需要传Header,就需要使用exchange(),这个方法中可以执行CRUD等各种方法,get、post等请求是基于execute、而execute基于doExecute;exchange是直接基于doExecute的。
另外这里就需要用的RequestEntity与ResponseEntity了。
在RequestEntity设置所需要的头,然后通过exchange()调用,最后返回ResponseEntity。
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:8080/coffee/?name={name}")
.build("mocha");
RequestEntity<Void> req = RequestEntity.get(uri)
.accept(MediaType.APPLICATION_XML)
.build();
ResponseEntity<String> resp = restTemplate.exchange(req, String.class);
Get集合
不论getEntity、getObject、exchange获取时,都传入了一个class对象,对于getList的操作,需要传入的是ParameterizedTypeReference
ParameterizedTypeReference<List<Coffee>> ptr =new ParameterizedTypeReference<List<Coffee>>() {};
String coffeeUri = "http://localhost:8080/coffee/";
ResponseEntity<List<Coffee>> list = restTemplate.exchange(coffeeUri, HttpMethod.GET, null, ptr);
这个使用的是exchange的重载函数null参数为RequestEntity。
定制
RestTemplate支持的HTTP库
RestTemplate支持多种不同的Http客户端库,它们统一的接口是ClientHttpRequestFactory
包括:
-
SimpleClientHttpRequestFactory【默认】
-
HttpComponentsClientHttpRequestFactory
ApacheHttpComponets
-
OkHttp3ClientHttpRequestFactory
OkHttp,OkHttp支持了Http2的实现
下边主要使用HttpComponentsClientHttpRequestFactory来进行配置
底层的参数
-
连接池
PoolingHttpClientConnectionManager
-
KeepAlive策略
这里根据response中Keep-Alive
设置的timeout进行设置,如果没设置,默认30s。
public class CustomConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
private final long DEFAULT_SECONDS = 30;
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
return Arrays.asList(response.getHeaders(HTTP.CONN_KEEP_ALIVE))
.stream()
.filter(h -> StringUtils.equalsIgnoreCase(h.getName(), "timeout")
&& StringUtils.isNumeric(h.getValue()))
.findFirst()
.map(h -> NumberUtils.toLong(h.getValue(), DEFAULT_SECONDS))
.orElse(DEFAULT_SECONDS) * 1000;
}
}
Spring提供了DefaultConnectionKeepAliveStrategy,在这里有 Keep-Alive 认里面的值,没有的话永久有效。这里定制的对没有进行了调整。
-
超时设置
通过setConnectTimeout与setReadTimeout对连接超时、读取超时进行设置,防止客户端受到网络问题的影响
-
禁用自动重试
由于幂等的要求自动重试会造成不必要的麻烦,这里也进行了禁用disableAutomaticRetries
代码:
@Bean
public HttpComponentsClientHttpRequestFactory requestFactory() {
PoolingHttpClientConnectionManager connectionManager =
new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(20);
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionManager(connectionManager)
.evictIdleConnections(30, TimeUnit.SECONDS)
.disableAutomaticRetries()
.setKeepAliveStrategy(new CustomConnectionKeepAliveStrategy())
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory(httpClient);
return requestFactory;
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
.setConnectTimeout(Duration.ofMillis(100))
.setReadTimeout(Duration.ofMillis(500))
.requestFactory(this::requestFactory)
.build();
}