首页 > 编程 > Java > 正文

RestTemplate集成Ribbbon的示例代码

2019-11-26 09:46:06
字体:
来源:转载
供稿:网友

上一篇文章我们分析了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();

只要加入注解@LoadBalancedRestTemplate会被注入,在没有引入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去处理,核心逻辑:

@Overridepublic 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@LoadBalancedpublic 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次。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表