Spring Cloud Eureka Ribbon

约定不等于承诺〃 2021-09-16 21:01 341阅读 0赞

Spring Cloud基础教程[Eureka集群,Ribbon负载均衡]

Spring Cloud Ribbon

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架, 它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个 SpringCloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon 实现的工具。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要。

在这一章中,我们将具体介绍如何使用Ribbon来实现客户端的负钱均衡,并且通过源码分析来了解Ribbon实现客户端负载均衡的基本原理.

客户端负载均衡

负载均衡在系统架构中是一个非常重要,并且是不得不去实施的内容因为负载均衡是对系统的高可用、M络压力的缓解和处理能力扩容的重要手段之一。我们通常所说的负载均衡都指的是服务端负载均衡,其中分为硬件负载均衡和软件负载均衡。硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,比如F5等:而软件负载均衡则是通过在服务器上安装一些具有均衡负载功能或模块的软件来完成请求分发工作,比如Nginx等。不论采用硬件负载均衡还是软件负载均衡,只要是服务端负载均衡都能以类似下图的架构力+忒构建起来:

20181102200243570.jpg

硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点,当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如线性轮询、按权审负我、按流最负载等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。而客户端负载均衡和眼务端负或均衡M大的不同点在于上面所提到的服务清单所存储的位置。在客尸端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这服务端的清单来自于服务注册中心,比如上一章我们介绍的Eureka服务端.同服务端负载均 衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性,只是这〜步骒需要与服务注册中心配合完成。在Spring Cloudd实现服务治理框架中,默认会创建针对各 个服务’冶理框架的Ribbon自动化整合配置,比如Eureka中 的org.springframework. cloud.netflix. ribbon.eureka.RibbonEurekaAutoConfiguration,Consul中的org.springframework.cloud.consul.discovery.RibbonConsulAuto-Configuration。在实际使用的时候,我们可以通过查看这两个类的实现,以找到它们 的配置详情来帮助我们更好地使用它。通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负找均衡调用非常 简单,只需要如下两步:

  1. 服务提供者R潘要启动多个服务实例并注册到一个注册中心或是多a相关联的服务注册中心。
  2. 服务消费者直接通过调用注解修饰过的 RcstTemplate来实现面 向服务的接口调用。

这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现.

动手试一试 【Dalston版】

下面我们通过具体的例子来看看如何使用Spring Cloud Ribbon来实现服务的调用以及客户端均衡负载。

下面的例子,我们将利用之前构建的eureka-server作为服务注册中心(3个节点)、springcloud-provider作为服务提供者作为基础。而基于Spring Cloud Ribbon实现的消费者,我们可以根据springcloud-consumer实现的内容进行简单改在就能完成,具体步骤如下:

根据上一篇文章的基础上进行直接负载均衡:https://blog.csdn.net/weixin_40470497/article/details/83588369 ,上一篇文章只是创建了一个提供者端口为:8080,在创建相同的服务提供者springcloud-provider2,在application.yml配置文件中修改一下端口:8081

项目已上传github:git@github.com:13849141963/spring-cloud.git

直接搭建客户端进行负载均衡,客户端为:springcloud-consumer,pom.xml文件内容如下:

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>1.5.13.RELEASE</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>
  7. <dependencies>
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter</artifactId>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-test</artifactId>
  15. <scope>test</scope>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-web</artifactId>
  20. </dependency>
  21. <!--eureka服务注册中心-->
  22. <dependency>
  23. <groupId>org.springframework.cloud</groupId>
  24. <artifactId>spring-cloud-starter-eureka-server</artifactId>
  25. </dependency>
  26. <!--ribbon基于客户端的负载均衡器-->
  27. <dependency>
  28. <groupId>org.springframework.cloud</groupId>
  29. <artifactId>spring-cloud-starter-ribbon</artifactId>
  30. </dependency>
  31. <dependency>
  32. <groupId>com.alibaba</groupId>
  33. <artifactId>fastjson</artifactId>
  34. <version>1.2.44</version>
  35. </dependency>
  36. </dependencies>
  37. <dependencyManagement>
  38. <dependencies>
  39. <dependency>
  40. <groupId>org.springframework.cloud</groupId>
  41. <artifactId>spring-cloud-dependencies</artifactId>
  42. <version>Dalston.RC1</version>
  43. <type>pom</type>
  44. <scope>import</scope>
  45. </dependency>
  46. </dependencies>
  47. </dependencyManagement>
  • 添加配置类:在RestTemplate增加@LoadBalanced注解:

    @Configuration
    public class ConfigBean {

    1. @Bean
    2. @LoadBalanced//基于客户端的负载均衡器
    3. public RestTemplate restTemplate() {
    4. return new RestTemplate();
    5. }
    6. //将jackson转化成fastjson
    7. @Bean
    8. public HttpMessageConverters fastJsonConfigure(){
    9. FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
    10. FastJsonConfig fastJsonConfig = new FastJsonConfig();
    11. fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
    12. //日期格式化
    13. fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
    14. converter.setFastJsonConfig(fastJsonConfig);
    15. return new HttpMessageConverters(converter);
    16. }

    }

  • 修改Controller。去掉原来通过LoadBalancerClient选取实例和拼接URL的步骤,直接通过RestTemplate发起请求。

    @RestController
    public class UserController {

    1. //服务的应用名称
    2. private final String url = "http://springcloud-provider";
    3. //使用restTemplate对rest接口进行调用 封装的对象
    4. //RestTemplate对象提供了多种便捷访问远程http服务的方法 是一种简单便捷的restful服务模板类,是spring提供的用于访问rest服务的客户端模板类
    5. @Autowired
    6. private RestTemplate restTemplate;
    7. //调用服务端的查询所有的服务
    8. @RequestMapping(value = "/queryAll")
    9. public Object queryAll(){
    10. System.out.println("==========进入访问方法============");
    11. List forObject = restTemplate.getForObject(url+"/queryAll", List.class);
    12. return forObject;
    13. }

    }

通过地址栏交替发送GET请求服务的消费者:10.0.45.103:8989/spring-consumer/queryAll。可以在分别两个服务的提供者控制上看到打印如下信息,Roibbon输出了当前客户端维护的springcloud-provider服务列表情况,其中包含了两个实例的位置,Ribbon就是按照此信息进行轮询访问,已实现基于客户端的负载均衡。另外还输出以一些非常有用的消息,如对各个实例的请求总数量,第一次连接信息,上一次连接信息,总的请求失败数量等。如图所示:watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDQ3MDQ5Nw_size_16_color_FFFFFF_t_70

假如一台服务器宕机了,客户端基于负载均衡访问还是能轮询到宕机的服务器,因为注册中心不能立刻发现服务宕机,默认30秒后就会发现,此时客户端请求注册中心就会出现一台服务的地址,这种问题可以配合springcloud Hystrix熔断器去做。

Ribbon负载均衡策略

1.BestAvailableRule 选择一个最小的并发请求的server,逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server

2.AvailabilityFilteringRule 过滤掉那些因为一直连接失败的被标记为circuit,tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值) 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status 里记录的各个server的运行状态

3.WeightedResponseTimeRule 根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成status时,使用roubine策略选择server。
4.RetryRule 对选定的负载均衡策略机上重试机制。在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server

5.RoundRobinRule roundRobin方式轮询选择server,轮询index,选择index对应位置的server

6.RandomRule 随机选择一个server,在index上随机,选择index对应位置的server

7.ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server,使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有 server),AvailabilityPredicate用于过滤掉连接数过多的Server。

自定义策略

这里我们队客户端ribbon模块进行修改:假定这次修改为随机访问RandomRule。

案例如下:

修改配置文件

  1. server:
  2. port: 8989
  3. tomcat:
  4. uri-encoding: UTF-8
  5. context-path: /springcloud-consumer
  6. eureka:
  7. client:
  8. service-url:
  9. defaultZone: http://peer1:1111/eureka/,http://peer3:3333/eureka/,http://peer2:2222/eureka/
  10. spring:
  11. application:
  12. name: springcloud-consumer
  13. http:
  14. encoding:
  15. enabled: true
  16. charset: UTF-8
  17. force: true
  18. #自定义ribbon的负载均衡策略
  19. client:
  20. ribbon:
  21. NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

在配置文件中,我们指明了本项目springcloud-consumer服务要使用 com.netflix.loadbalancer.RandomRule 策略。

修改启动类,加了实例化与配置文件对应的策略类。

  1. @Configuration
  2. public class ConfigBean {
  3. @Bean
  4. @LoadBalanced//基于客户端的负载均衡器
  5. public RestTemplate restTemplate() {
  6. return new RestTemplate();
  7. }
  8. //将jackson转化成fastjson
  9. @Bean
  10. public HttpMessageConverters fastJsonConfigure(){
  11. FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
  12. FastJsonConfig fastJsonConfig = new FastJsonConfig();
  13. fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
  14. //日期格式化
  15. fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
  16. converter.setFastJsonConfig(fastJsonConfig);
  17. return new HttpMessageConverters(converter);
  18. }
  19. //自定义ribbon的负载均衡策略
  20. @Bean
  21. public IRule ribbonRule() {
  22. return new RandomRule();//实例化与配置文件对应的策略类
  23. }
  24. }

编写controller

  1. @RestController
  2. public class UserController {
  3. private final String url = "http://springcloud-provider";
  4. //使用restTemplate对rest接口进行调用 封装的对象
  5. //RestTemplate对象提供了多种便捷访问远程http服务的方法 是一种简单便捷的restful服务模板类,是spring提供的用于访问rest服务的客户端模板类
  6. @Autowired
  7. private RestTemplate restTemplate;
  8. @Autowired
  9. private LoadBalancerClient loadBalancerClient;
  10. //调用服务端的查询所有的服务
  11. @RequestMapping(value = "/cs")
  12. public Object cs(){
  13. System.out.println("==========进入访问方法============");
  14. this.loadBalancerClient.choose("client");//随机访问策略
  15. List forObject = restTemplate.getForObject(url+"/queryAll", List.class);
  16. return forObject;
  17. }
  18. }

启动测试通过服务的提供者springcloud-provider和springcloud-provider2的控制台可以发现于随机数与轮训[默认]的方式有明显的不同。总结一句:根据不同的业务需求选择不同的负载均衡策略.

重试机制

大多数情况下,上面的实现没有任何问题,但是总有一些意外发生,比如:有一个实例发生了故障而该情况还没有被服务治理机制及时的发现和剔除,这时候客户端访问该节点的时候自然会失败。所以,为了构建更为健壮的应用系统,我们希望当请求失败的时候能够有一定策略的重试机制,而不是直接返回失败。这个时候就需要开发人员人工的来为上面的RestTemplate调用实现重试机制。

1、首先我们启动eureka集群

2、再启动服务的提供者springcloud-provider,服务的提供者准备两台

3、通过配置客户端ribbon负载均衡调用服务端,我们将一台服务的提供者宕机,通过客户端调用发现当调用到宕机的那台服务提供者,客户端就会抛出异常信息无法进行连接:Connection refused: connect

4、我们来配置下客户端的重试机制,Spring Cloud整合了Spring Retry来实现重试逻辑,而对于开发者只需要做一些配置即可。

在客户端添加如下配置:

  1. spring.cloud.loadbalancer.retry.enabled=true
  2. hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
  3. hello-service.ribbon.ConnectTimeout=250
  4. hello-service.ribbon.ReadTimeout=1000
  5. hello-service.ribbon.OkToRetryOnAllOperations=true
  6. hello-service.ribbon.MaxAutoRetriesNextServer=2
  7. hello-service.ribbon.MaxAutoRetries=1

各项参数说明:

1)、spring.cloud.loadbalancer.retry.enabled:该参数用来开启重试机制,它默认是关闭的。这里需要注意,官方文档中的配置参数少了enabled。
2)、hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:断路器的超时时间需要大于ribbon的超时时间,不然不会触发重试。
3)、hello-service.ribbon.ConnectTimeout:请求连接的超时时间
4)、hello-service.ribbon.ReadTimeout:请求处理的超时时间
5)、hello-service.ribbon.OkToRetryOnAllOperations:对所有操作请求都进行重试
6)、hello-service.ribbon.MaxAutoRetriesNextServer:切换实例的重试次数
7)、hello-service.ribbon.MaxAutoRetries:对当前实例的重试次数

5、根据如上配置,当访问到服务提供者故障的时候,它会再尝试访问一次当前实例(次数由MaxAutoRetries配置),如果不行,就换一个实例进行访问,在这里我只是模仿一台提供者宕机。如果两台服务的提供者都宕机了,先进行访问,访问失败再换一次实例访问(更换次数由MaxAutoRetriesNextServer配置),如果依然不行,返回失败信息。

发表评论

表情:
评论列表 (有 0 条评论,341人围观)

还没有评论,来说两句吧...

相关阅读