Administrator
发布于 2023-04-21 / 14 阅读
0
0

微服务(四)——Ribbon负载均衡

负载均衡流程

上一节我们主要学习了什么是Eureka,服务注册与发现,以及负载均衡的简单实现。那你知道在Eureka中负载均衡是怎么实现的吗?我们能直接请求到http://userservice/user/1这个地址吗?

答案是不能的,这并不是一个直接可请求的地址。那为什么我们使用Eureka能成功请求呢,这就涉及到本章要学习的重点:Ribbon负载均衡。

目前主流的负载均衡有两种:

  • 集中式负载均衡:代理方式进行负载,如nginx
  • 客户端负载均衡:Ribbon就属于客户端自己做负载均衡,它和Eureka都属于Nefflix提供的服务于SpringCloud微服务的组件。

负载均衡大致流程:

  • order-service发起调用请求http://userservice/user/1到Ribbon
  • Ribbon询问Eureka注册中心是否有userservice这个服务,有的话从注册中心拉取
  • Ribbon获取到服务列表localhost:8081和localhost:8082
  • 最后通过负载均衡策略选择一个服务,比如这里选择了localhost:8081,那么http://userservice/user/1的请求实际是转发给了http://localhost:8081/user/1,所以成功获取到了数据。

Ribbon源码分析

前面我们知道,需要给RestTemplate加上@LoadBalanced注解,那么它有什么作用呢。它的作用是标记RestTemplate发起的请求需要被Ribbon拦截和处理。拦截动作是谁完成的呢,LoadBalancerInterceptor。

LoadBalancerInterceptor实现了ClientHttpRequestInterceptor接口,看这个接口的名字就知道,它是一个客户端http请求的拦截器,我们通过的RestTemplate发起的正是http请求,所以会被它拦截,而LoadBalancerInterceptor继承了这个接口并实现了自己的intercept拦截方法。

接下来,我们发起一个请求http://localhost:8080/order/101,打个断点看一下

  • 进入LoadBalancerInterceptor的intercept拦截方法
  • 下一步,进入RibbonLoadBalancerClient的execute方法,DynamicServerListLoadBalancer是一个动态服务列表负载均衡器,它会通过serviceId向Eureka注册中心拉取userservice,展开可以看到,这里已经获取到了userservice的服务列表
  • 接下来下一步,进入getServer方法,它会判断loadBanlancer是否为空,不为空的话调用ZoneAwareLoadBalancer的chooseServer方法,ZoneAwareLoadBalancer继承了DynamicServerListLoadBalancer
  • 进入ZoneAwareLoadBalancer的chooseServer方法,往下走,可以看到,它调用了父类的chooseServer方法
  • 继续往下,进入BaseLoadBalancer的chooseServer方法
  • 可以看到,这里有一个rule,跳转到rule变量,它是IRule类型,看名字可以明白它是一个规则接口,我们通过ctrl+h查看下它的实现类。主要有轮询、随机、重试等规则,默认是ZoneAvoidanceRule。

    rule.choose(key)方法可以选择负载均衡的规则,上图可以看到key是default,因为我们没显示得指定规则,所以选择的是默认的ZoneAvoidanceRule规则。
  • 继续往下,可以看到这里成功拿到一个userservice服务
  • 接下来就是成功返回了,可以看到浏览器成功获取到了数据,并且请求是转发给了localhost:8081服务

  • 这样,我们应该大概了解Ribbon负载均衡的请求流程了。大家可以多试几次,看看请求分发的效果。

不想看源码的可以看下下面的负载均衡流程图:


负载均衡策略

上面我们也看到了,负载均衡的规则主要是有IRule的实现类决定的。我们看下IRule的框架和各规则类的描述

我们看下ZoneAvoidanceRule(默认规则),这里的Zone可以理解为一个机房或区域,我们可以设置Zone值让服务消费者获取最佳区域的服务列表,再做轮询请求访问。但是,对绝大部分中小型公司来说,是没有这么多的资源的,基本就是默认设置,等同于RoundRobinRule轮询策略。

那么我们怎么改变服务中的负载均衡规则呢,有两种方式:

  1. 代码方式:自定义一个IRule(当然,也可以重新定义一个配置类返回bean,这里写在启动类里为了方便)
    @MapperScan("cn.lsy.order.mapper")
    @SpringBootApplication
    public class OrderApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(OrderApplication.class, args);
        }
    
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
        @Bean
        public IRule rule(){
            //随机策略
            return new RandomRule();
        }
    
    }

     

  2. 文件方式:order-service的application.yml里添加负载均衡规则配置:
     

    userservice:
      ribbion:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandonRule #负载均衡规则

    (两者有什么区别呢?代码方式配置灵活,全局规则,但修改时需要重新打包发布。配置方式更直观、方便,无需重新打包发布,但是需要指定服务名,如userservice,无法全局配置。)
    大家自己去测试一下吧,看看是不是随机请求的效果~

自定义负载均衡规则的话可以自定义一个规则类,实现IRule接口或者继承AbstractLoadBalancerRule类,这里就先不展开了。

饥饿加载

Ribbon默认是懒加载,第一次请求访问比较慢,因为它会判断请求的服务列表是否为空,然后进行去Eureka注册中心拉取的初始化动作。

需要加快首次请求速度的话可以在配置文件开启饥饿加载,order-service的application.yml中配置

ribbon:
  eager-load:
    enabled: true #开启饥饿加载
    clients:  #指定服务名称
      - userservice

重启order-service服务,可以看到,order-service启动时就已经完成了userservice服务列表的拉取。所以,后续请求速度就会比较快了。


评论