RestTemplate集成Ribbbon的示例代碼
上一篇文章我們分析了ribbon的核心原理,接下來我們來看看springcloud是如何集成ribbon的,不同的springcloud的組件(feign,zuul,RestTemplate)集成ribbon有所不同,這篇文章先來看看RestTemplate。
RestTemplate的類圖如下

HttpAccessor主要根據(jù)ClientHttpRequestFactory創(chuàng)建ClientHttpRequestInterceptingHttpAccessor擴(kuò)展了HttpAccessor,創(chuàng)建攔截的InterceptingClientHttpRequest,這里會(huì)設(shè)置攔截器ClientHttpRequestInterceptor,這是集成ribbon的核心,當(dāng)RestTemplate發(fā)起http請求調(diào)用的時(shí)候,會(huì)先經(jīng)過攔截器,然后才真正發(fā)起http請求。
攔截器ClientHttpRequestInterceptor是如何被設(shè)置的呢?在LoadBalancerAutoConfiguration類中,有如下代碼:
@LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList();
只要加入注解@LoadBalanced的RestTemplate會(huì)被注入,在沒有引入spring retry組件的時(shí)候,加載如下配置:
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
這樣RestTemplate就被設(shè)置了LoadBalancerInterceptor,下面來看看整個(gè)調(diào)用過程

整個(gè)過程有點(diǎn)復(fù)雜,核心就是經(jīng)過攔截器LoadBalancerInterceptor,通過RibbonLoadBalancerClient發(fā)起負(fù)載均衡調(diào)用。RibbonLoadBalancerClientI組合了LoadBalancer,所以具備了負(fù)載均衡的能力,也就是我們在上一篇文章解讀的ribbon原理。
圖中我們沒有畫出真正發(fā)起http請求的過程,其默認(rèn)是由SimpleClientHttpRequestFactory創(chuàng)建,ClientHttpRequestFactory的類圖如下:

從調(diào)用時(shí)序圖上我們看到,開始我們調(diào)用的是InterceptingClientHttpRequestFactory來獲取InterceptingClientHttpRequest,它們通過組合的方式集成了ClientHttpRequestFactory和攔截器,InterceptingClientHttpRequest發(fā)起調(diào)用的時(shí)候委托了其內(nèi)部類InterceptingRequestExecution去處理,核心邏輯:
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}else {
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
List<String> values = entry.getValue();
for (String value : values) {
delegate.getHeaders().add(entry.getKey(), value);
}
}
if (body.length > 0) {
StreamUtils.copy(body, delegate.getBody());
}
return delegate.execute();
}
}
首先會(huì)先取出攔截器集合的第一個(gè)執(zhí)行,當(dāng)攔截器執(zhí)行完成后,會(huì)回調(diào)回來,執(zhí)行else的代碼,真正發(fā)起http請求,主要有兩種方式實(shí)現(xiàn)ClientHttpRequestFactory接口:
- 一種是
SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)創(chuàng)建底層的Http請求連接 - 一種方式是使用
HttpComponentsClientHttpRequestFactory方式,底層使用HttpClient訪問遠(yuǎn)程的Http服務(wù),使用HttpClient可以配置連接池和證書等信息。
RestTemplate默認(rèn)是使用SimpleClientHttpRequestFactory,內(nèi)部是調(diào)用jdk的HttpConnection,默認(rèn)超時(shí)為-1,可以這樣設(shè)置超時(shí)時(shí)間:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(1000 * 2);//連接超時(shí)時(shí)間
factory.setReadTimeout(1000 * 1);//讀超時(shí)時(shí)間
return new RestTemplate(factory);
}
使用HttpComponentsClientHttpRequestFactory方式可以使用連接池(推薦) ,還可以設(shè)置重試策略(具體沒有研究過)
如果想開啟重試機(jī)制,我們可以引入spring的retry組件
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>版本號</version> </dependency>
這樣springcloud-ribbon就會(huì)加重如下配置:
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration {
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate template = new RetryTemplate();
template.setThrowLastExceptionOnExhausted(true);
return template;
}
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() {
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
}
}
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryInterceptorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
LoadBalancerRequestFactory requestFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
lbRetryPolicyFactory, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
@Bean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory(SpringClientFactory clientFactory) {
return new RibbonLoadBalancedRetryPolicyFactory(clientFactory);
}
攔截器替換成RetryLoadBalancerInterceptor了,這里集成了retry組件retryTemplate。重試策略由RetryHandler接口來配置,默認(rèn)實(shí)現(xiàn)類DefaultLoadBalancerRetryHandler,如下為默認(rèn)的配置參數(shù)
#最大的重試次數(shù) ribbon.MaxAutoRetries=0 #最大重試server的個(gè)數(shù) ribbon.MaxAutoRetriesNextServer=1 #是否開啟任何異常都重試(默認(rèn)在get請求下會(huì)重試,其他情況不會(huì)重試,除非設(shè)置為true) ribbon.OkToRetryOnAllOperations=false #指定重試的http狀態(tài)碼 ribbon.retryableStatusCodes=500,501
以上是對全局生效,如果加上xxx.ribbon.MaxAutoRetries=1這樣只會(huì)對某個(gè)ribbon客戶端生效。MaxAutoRetries和MaxAutoRetriesNextServer是配合使用的,最大重試次數(shù)是針對每一個(gè)server的,如果設(shè)置MaxAutoRetries=1,MaxAutoRetriesNextServer=1這樣觸發(fā)最大重試次數(shù)就是4次。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java?嵌入數(shù)據(jù)引擎從?SQLite?到?SPL詳解
這篇文章主要介紹了Java?嵌入數(shù)據(jù)引擎:從?SQLite?到?SPL,SQLite架構(gòu)簡單,其核心雖然是C語言開發(fā)的,但封裝得比較好,對外呈現(xiàn)為一個(gè)小巧的Jar包,能方便地集成在Java應(yīng)用中,本文給大家介紹的非常詳細(xì),需要的朋友參考下2022-07-07
SpringBoot3.x打包Docker容器的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot3.x打包Docker容器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
SpringSecurity?用戶帳號已被鎖定的問題及解決方法
這篇文章主要介紹了SpringSecurity?用戶帳號已被鎖定,本文給大家分享問題原因及解決方式,需要的朋友可以參考下2023-12-12
SpringBoot項(xiàng)目啟動(dòng)健康檢查的操作方法
在現(xiàn)代的微服務(wù)架構(gòu)中,容器化技術(shù)已經(jīng)成為一種主流的部署方式,Docker 作為容器化技術(shù)的代表,提供了一種輕量級、可移植的解決方案,然而,僅僅將應(yīng)用容器化是不夠的,我們還需要確保這些容器在運(yùn)行時(shí)能夠保持健康狀態(tài),這就是健康檢查發(fā)揮作用的地方2024-12-12
SpringBoot?thymeleaf實(shí)現(xiàn)餅狀圖與柱形圖流程介紹
這篇文章主要介紹了SpringBoot?thymeleaf實(shí)現(xiàn)餅狀圖與柱形圖流程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-12-12
java 數(shù)據(jù)結(jié)構(gòu)之堆排序(HeapSort)詳解及實(shí)例
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)之堆排序(HeapSort)詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03

