微服务:SpringCloud 添加ribbon 负载均衡(+源码分析!)

淩亂°似流年 2023-02-12 05:39 94阅读 0赞

在之前注册中心那一章,消费者调用提供者的接口,是需要写死这样的url的。但是真实的环境我们一般要求服务提供者高可用,所以呢同一个服务需要多个服务提供者。那么消费者就不能够写死这个url了。所以基于如此我们改一下代码。

一、修改代码,添加ribbon实现负载均衡

1、服务提供者

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70

首先呢,用ip+port用于区别调用不同的服务。设置到ProductName

我们用两个application分别启动同一个项目, 不过用的是不同端口,模拟两个服务。记得修改端口。当然如果不会的话,就copy一下再建立一个子项目。

2、服务消费者

这个@LoadBalanced是实现负载均衡的关键!所以restTemplate调用的时候就可以按负载均衡来走。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 1

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 2

替换成相应的服务的名字,就是在服务yml中配置的应用名。

之后来测试

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 3

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 4

2、关于ribbon介绍

ribbon 是 Netflflixfa 发布的一个负载均衡器,有助于控制 HTTP 和 TCP 客户端行为。在 SpringCloud 中, Eureka一般配合 Ribbon 进行使用, Ribbon 提供了客户端负载均衡的功能, Ribbon 利用从 Eureka 中读 取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载。

在 SpringCloud 中可以将注册中心和 Ribbon 配合使用, Ribbon 自动的从注册中心中获取服务提供者的 列表信息,并基于内置的负载均衡算法,请求服务。

主要作用:

1 )服务调用:基于Ribbon 实现服务调用, 是通过拉取到的所有服务列表组成(服务名 - 请求路径的)映射关系。借助 RestTemplate 最终进行调用

2 )负载均衡:当有多个服务提供者时,Ribbon 可以根据负载均衡的算法自动的选择需要调用的服务地址。

3、负载均衡策略

Ribbon 内置了多种负载均衡策略,内部负责复杂均衡的顶级接口为 com.netflix.loadbalancer.IRule

  1. com.netflix.loadbalancer.RoundRobinRule :以轮询的方式进行负载均衡。
  2. com.netflix.loadbalancer.RandomRule :随机策略
  3. com.netflix.loadbalancer.RetryRule :重试策略。
  4. com.netflix.loadbalancer.WeightedResponseTimeRule :权重策略。会计算每个服务的权重,越高的被调用的可能性越大。
  5. com.netflix.loadbalancer.BestAvailableRule :最佳策略。遍历所有的服务实例,过滤掉故障实例,并返回请求数最小的实例返回。
  6. com.netflix.loadbalancer.AvailabilityFilteringRule :可用过滤策略。过滤掉故障和请求数超过阈值的服务实例,再从剩下的实力中轮询调用。

在服务消费者的application.yml配置文件中修改负载均衡策略

  1. ##需要调用的微服务名称
  2. shop-service-product:
  3. ribbon:
  4. NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

策略选择:

1 、如果每个机器配置一样,则建议不修改策略 ( 推荐 )。

2 、如果部分机器配置强,则可以改为 WeightedResponseTimeRule。

4、重试机制

比如我们现在有一个消费者A,两个提供者B-1、B-2(都是提供同一个服务,高可用),现在采取负载均衡策略的是轮询,如果此时访问的B-1,B-1挂掉了或者是由于网络不好超过了20ms(默认),那么就会报错!所以我们需要开启重试机制就会起作用向另一个服务B-2发送一次请求。下面是关于重试机制的配置。

  1. <dependency>
  2. <groupId>org.springframework.retry</groupId>
  3. <artifactId>spring-retry</artifactId>
  4. </dependency>
  5. service-product:
  6. ribbon:
  7. #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  8. ConnectTimeout: 250 # Ribbon的连接超时时间
  9. ReadTimeout: 1000 # Ribbon的数据读取超时时间
  10. OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
  11. MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
  12. MaxAutoRetries: 1 # 对当前实例的重试次数

5、负载均衡的源码解析

20200524183956490.png

为什么这个方法上面加入了一个标签@LoadBalanced就可以实现负载均衡了呢?难道是这个标签有啥特别之处。大家可以点进去。没啥特别之处!这里只是一个标记,以至于我们在拦截器进行拦截的时候可以找到哪些是需要负载均衡的。

其实主要还是我们之前说的springboot的自动装载机制。去瞅一眼依赖。

20200524184501652.png

20200524184509678.png

我们进入这个类

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 5

实际上,在加载这个类之前要加载LoadBalancerAutoConfiguration这个类。

这个类,主要做了三件事情:

(1)创建了一个 LoadBalancerInterceptor 的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 6

(2)创建了一个 RestTemplateCustomizer 的Bean,用于给 RestTemplate 增加 LoadBalancerInterceptor 拦截器。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 7

(3)维护了一个被 @LoadBalanced 注解修饰的 RestTemplate 对象列表,并在这里进行初始化,通过调用 RestTemplateCustomizer 的实例来给需要客户端负载均衡的 RestTemplate 增加LoadBalancerInterceptor 拦截器。

20200524192802706.png

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 8

实际上这个才是最重要的真大佬!!!一会我们点进去康康 ,先不要着急

这个时候我们调用restTemplate.getForObject(“http://service-product:” +”/product/1”,Product.class);就会进入我们的拦截器。

接着我们看一下这个拦截器LoadBalancerInterceptor的拦截器方法。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 9

拦截器拦截执行的代码都在intercept方法里,看一下excute方法。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 10

这里面创建了一个LoadBalancer对象,其实就是负载均衡器(含有负载均衡算法)。IloadBalancer其实是一个接口,主要是实现类BaseLoadBalancer。

看一下getServer方法:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 11

点进去chooseServer方法。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 12

实际上rule就是负载均衡采用的算法,默认是轮询

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 13

我们来debug调试一下。我们打两个断点

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 14

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 15

当order-service启动的时候就到了第一个断点。添加一个负载均衡拦截器。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 16

现在我们到第二个断点,输入http://localhost:9002/order/buy/1。这里面就拦截到。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 17

再打一个断点到

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 18

这样就获取到了

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDU4ODQ5NQ_size_16_color_FFFFFF_t_70 19

负载均衡要访问的服务的地址。

发表评论

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

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

相关阅读