RestTemplate集成Ribbbon的示例代码

 更新时间:2018年08月03日 10:34:39   作者:杨辉  
这篇文章主要介绍了RestTemplate集成Ribbbon的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

上一篇文章我们分析了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去处理,核心逻辑:

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

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

相关文章

  • 深入探究如何使用Java编写MapReduce程序

    深入探究如何使用Java编写MapReduce程序

    MapReduce是一种用于处理大规模数据集的并行编程模型,其特点高效性和可扩展性,在本文中,我们将深入了解MapReduce,并使用Java编写一个简单的MapReduce程序,需要的朋友可以参考下
    2023-05-05
  • Java简单实现猜数字游戏附C语言版本

    Java简单实现猜数字游戏附C语言版本

    猜数字是兴起于英国的益智类小游戏,起源于20世纪中期,一般由两个人或多人玩,也可以由一个人和电脑玩。游戏规则为一方出数字,一方猜,今天我们来用Java和C语言分别把这个小游戏写出来练练手
    2021-11-11
  • JAVA实现LRU算法的参考示例

    JAVA实现LRU算法的参考示例

    这篇文章主要介绍了JAVA实现LRU算法的参考示例,帮助大家根据需求实现算法,感兴趣的朋友可以了解下
    2020-10-10
  • Java连接PostgreSql数据库及基本使用方式

    Java连接PostgreSql数据库及基本使用方式

    这篇文章主要介绍了Java连接PostgreSql数据库及基本使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • java8列表中通过stream流根据对象属性去重的三种方式

    java8列表中通过stream流根据对象属性去重的三种方式

    这篇文章主要介绍了java8列表中通过stream流根据对象属性去重的三种方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • SpringBoot启动时如何通过启动参数指定logback的位置

    SpringBoot启动时如何通过启动参数指定logback的位置

    这篇文章主要介绍了SpringBoot启动时如何通过启动参数指定logback的位置,在spring boot中,使用logback配置的方式常用的有两种,本文给大家介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • maven 删除下载失败的包的方法

    maven 删除下载失败的包的方法

    本文介绍了当Maven包报红时,使用删除相关文件的方法来解决该问题,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • java常用API介绍之包装类

    java常用API介绍之包装类

    这篇文章主要介绍了java常用API介绍之包装类,API,即Application Programming Interface,中文名称是“应用程序接口",这些接口就是"jdk所提供"给我们使用的类,需要的朋友可以参考下
    2023-04-04
  • JVM的类加载过程以及双亲委派模型详解

    JVM的类加载过程以及双亲委派模型详解

    这篇文章主要介绍了JVM的类加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。,需要的朋友可以参考下
    2019-06-06
  • Java中String字符串常量池和intern方法源码分析

    Java中String字符串常量池和intern方法源码分析

    在之前的文章中,小编给大家介绍了String字符串的不可变性及其实现原理,其中给大家提到了字符串常量池的概念,那么什么是常量池,String字符串与常量池有什么关系,本文给大家唠唠字符串常量池及String#intern()方法的作用,需要的朋友可以参考下
    2023-05-05

最新评论