本文講的是springcloud是如何整合ribbon的,不同的springcloud的元件(feign,zuul,RestTemplate)整合ribbon有所不同,這篇文章先來看看RestTemplate。
RestTemplate的類別圖如下
#HttpAccessor
主要根據ClientHttpRequestFactory
建立ClientHttpRequest
InterceptingHttpAccessor
擴充了HttpAccessor
,建立攔截的InterceptingClientHttpRequest
,這裡會設定攔截器ClientHttpRequestInterceptor
,這是整合ribbon的核心,當RestTemplate
發起http請求呼叫的時候,會先經過攔截器,然後才真正發起http請求。
攔截器ClientHttpRequestInterceptor
是如何被設定的呢?在LoadBalancerAutoConfiguration
類別中,有以下程式碼:
@LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList();
只要加入註解@LoadBalanced
的RestTemplate
會被注入,在沒有引入spring retry元件的時候,載入如下配置:
@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
就被設定了LoadBalancerInterceptor,下面來看看整個呼叫過程
整個過程有點複雜,核心就是經過攔截器LoadBalancerInterceptor,透過RibbonLoadBalancerClient發起負載平衡呼叫。 RibbonLoadBalancerClientI組合了LoadBalancer,所以具備了負載平衡的能力,也就是我們在上一篇文章解讀的ribbon原理。
圖中我們沒有畫出真正發起http請求的過程,其預設是由SimpleClientHttpRequestFactory
創建,ClientHttpRequestFactory
的類別圖如下:
#從呼叫時序圖上我們看到,開始我們呼叫的是InterceptingClientHttpRequestFactory
來取得InterceptingClientHttpRequest
,它們透過組合的方式整合了ClientHttpRequestFactory
和攔截器,InterceptingClientHttpRequest
發起呼叫的時候委託了其內部類別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(); } }
首先會先取出攔截器集合的第一個執行,當攔截器執行完成後,會回調回來,執行else的程式碼,真正發起http請求,主要有兩種方式實作ClientHttpRequestFactory
介面:
一種是SimpleClientHttpRequestFactory
,使用J2SE提供的方式(既java.net套件提供的方式)建立底層的Http請求連線
一種方式是使用HttpComponentsClientHttpRequestFactory
方式,底層使用HttpClient存取遠端的Http服務,使用HttpClient可以設定連線池和憑證等資訊。
RestTemplate預設是使用SimpleClientHttpRequestFactory,內部是呼叫jdk的HttpConnection,預設逾時為-1,可以這樣設定逾時時間:
@Bean @LoadBalanced public RestTemplate restTemplate() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(1000 * 2);//连接超时时间 factory.setReadTimeout(1000 * 1);//读超时时间 return new RestTemplate(factory); }
使用HttpComponentsClientHttpRequestFactory#HttpRequestFactory#HttpComponentsClientHttpRequestFactory
方式可以使用連線池(推薦) ,還可以設定重試策略(具體沒有研究過)
如果想開啟重試機制,我們可以引入spring的retry元件
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>版本号</version> </dependency>
這樣springcloud-ribbon就會加重如下配置:
@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
介面來配置,預設實作類別DefaultLoadBalancerRetryHandler
,如下為預設的設定參數
#最大的重试次数 ribbon.MaxAutoRetries=0 #最大重试server的个数 ribbon.MaxAutoRetriesNextServer=1 #是否开启任何异常都重试(默认在get请求下会重试,其他情况不会重试,除非设置为true) ribbon.OkToRetryOnAllOperations=false #指定重试的http状态码 ribbon.retryableStatusCodes=500,501
以上是對全域生效,如果加上xxx.ribbon.MaxAutoRetries=1
這樣只會對某個ribbon客戶端生效。 MaxAutoRetries和MaxAutoRetriesNextServer是搭配使用的,最大重試次數是針對每一個server的,如果設定MaxAutoRetries=1,MaxAutoRetriesNextServer=1這樣觸發最大重試次數就是4次。
相關文章:
以上是詳細講解springcloud的組件之RestTemplate整合的Ribbbon的詳細內容。更多資訊請關注PHP中文網其他相關文章!