上一篇文章我们分析了ribbon的核心原理,接下来我们来看看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
方式可以使用连接池(推荐) ,还可以设置重试策略(具体没有研究过)
如果想开启重试机制,我们可以引入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次。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。