【微服务SpringCloud】:Ribbon 详解

1、什么是 Ribbon ?

Ribbon 是 Netflixfa 发布的一个负载均衡器,主要用于控制 HTTP 和 TCP客户端行为

Ribbon 有两个主要作用:

  • 服务调用:基于 Ribbon 实现服务调用, 是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。借助 RestTemplate 最终进行调用
  • 负载均衡:当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址

在服务消费方通过 RestTemplate 调用微服务的时候,会拼接服务的访问地址等信息,如下所示:
在这里插入图片描述
这种方式虽然可以,但是不太方便。所以可以使用 Ribbon 的服务调用来解决,在后面我们去了解具体使用方法。

Ribbon 可以单独使用,但是大多数情况下在 SpringCloud 中使用,配合注册中心进行使用。

2、什么是负载均衡 ?

负载均衡是一种基础的网络服务,按照指定的负载均衡算法,将流量分配到后端服务集群上,从而为系统提供并行扩展的能力。

  • 服务端负载均衡:发送请求到服务器,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配
  • 客户端负载均衡:客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问;即在客户端就进行负载均衡算法分配,Ribbon 就是一个客户端负载均衡器

在微服务中要保持系统的高可用行,所以将多个相同的服务提供者注册到注册中心上,那么服务消费者需要具体的选择服务提供者,这个过程就由 Ribbon 中的负载均衡完成。

3、Ribbon 中的组件

Ribbon 的关键组件如下:

  • ServerList:可以响应客户端的特定服务的服务器列表。

    <clientName>.ribbon.NIWSServerListFilterClassName配置项可以配置获取服务器的方法,在默认情况使用的是ConfigurationBasedServerList类,这个类可以从配置文件中获取所有服务列表:

    #这个名称表示具体的服务
    #供 RestTemplate 类去调用,如果省略服务名称则表示配置作用在所有的服务中,
    服务名称: 
    	ribbon: 
    		listOfServers: www.microsoft.com:80,www.yahoo.com:80,www.google.com:80
    

    但是配合注册中心时,服务列表是从注册中心获取的,所以上述配置可以省略,这时使用的是DiscoveryEnabledNIWSServerList类。

  • ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器。

    <clientName>.ribbon.NIWSServerListFilterClassName配置项可以配置过滤器使用的类,默认使用ZonePreferenceServerListFilter

  • ServerListUpdater:用于执行动态服务器列表更新。

  • Rule:负载均衡策略,用于确定从服务器列表返回哪个服务器。

    <clientName>.ribbon.NFLoadBalancerRuleClassName配置项可以配置负载均衡策略使用的类,默认使用ZoneAvoidanceRule

  • Ping:客户端用于快速检查服务器当时是否处于活动状态。

    <clientName>.ribbon.NFLoadBalancerPingClassName配置项可以配置检查服务器使用的类,默认使用DummyPing类,当和注册中心配合使用时,使用的是NIWSDiscoveryPing

  • LoadBalancer:负载均衡器,负责负载均衡调度的管理。将以上组件组合到这个类中一起工作。

    <clientName>.ribbon.NFLoadBalancerClassName配置项可以配置负载均衡器使用的类,默认使用ZoneAwareLoadBalancer

在上述几个组件中,最需要关注的就是 Rule 组件。

4、Rule 组件

Rule 组件用于负载均衡策略,Ribbon 提供了一个 IRule 接口是 Ribbon 中负载均衡的顶级接口。IRule 接口中定义了选择负载均衡策略的基本操作,可以通过配置文件或者配置类选择具体的负载均衡策略。

策略名称说明
com.netflix.loadbalancer.RoundRobinRule轮询策略按顺序选择服务,默认就是这种服务。
com.netflix.loadbalancer.RandomRule随机策略随机选择服务
com.netflix.loadbalancer.RetryRule重试策略在一个时间段选择服务失败,则选择尝试一个可用服务
com.netflix.loadbalancer.WeightedResponseTimeRule权重策略会计算每个服务的权重,越高的被调用的可能性越大。
com.netflix.loadbalancer.BestAvailableRule最佳策略遍历所有的服务实例,过滤掉故障实例,并返回请求数最小的实例返回。
com.netflix.loadbalancer.AvailabilityFilteringRule可用过滤策略过滤掉故障和请求数超过阈值的服务实例,再从剩下的实力中轮询调用
com.netflix.loadbalancer.ZoneAvoidanceRule复合策略使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个 zone的运行性能是否可用,剔除不可用的zone,后一个用于过滤掉连接数过多的 Server。

5、实现负载均衡

5.1、单独使用 Ribbon

  • 引入依赖:如果不配和注册中心使用,那么需要单独引用 Ribbon 的依赖。
    <dependencies>	
    	<!--Ribbon的依赖  -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>版本号</version>
        </dependency>
    </dependencies>
    
  • 修改启动类:向 IOC 容器中注入 RestTemplate 类,并添加 @LoadBalanced注解
    @SpringBootApplication
    public class Application {
    
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
        public static void main(String[] args)
        {
           SpringApplication.run(Application.class, args);
        }
    }
    
  • 修改配置文件:在配置文件中添加有关 Ribbon 的设置
    #microservice-provider-user 表示需要调用的服务名
    eurek-user-service:
      ribbon:
        listOfServers: localhost:9003,localhost:9004,localhost:9005
    
  • 具体使用:在使用``调用服务的时候,IP地址是一个字符串,不同于之前的具体的地址,这就是使用 Ribbon 的好处。
    @RestController
    @RequestMapping("/order")
    public class orderController {
    	
        @Autowired
        private RestTemplate restTemplate;
    
        @RequestMapping(value = "/buy", produces = "text/plain;charset=UTF-8")
        @ResponseBody
        public String buy(int id) throws IOException {
        	//这里的"http://microservice-provider-user/product/在没有使用 Ribbon之前,应该是 "http://localhost:9003/product/
        	//在使用了 Ribbon 之后就直接与配置文件中的配置对应,自动完成负载均衡
            return this.restTemplate.getForObject("http://microservice-provider-user/product/"+id,String.class);
        }
    

5.2、与注册中心配合使用

  • 引入依赖:当与注册中心使用时,可以省略对 Ribbon 依赖的导入,因为 Consul 和 Eureka 中已经含有 Ribbon 依赖。
  • 修改启动类:向 IOC 容器中注入 RestTemplate 类,并添加 @LoadBalanced注解
    @SpringBootApplication
    @EnableEurekaClient
    public class OrderMain{
    
        public static void main(String[] args)
        {
           SpringApplication.run(OrderMain.class, args);
        }
        
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
  • 设置赋值均衡策略:Ribbon 默认使用的是轮询策略,也可以通过如下配置替换轮询策略
    eurek-user-service:
     ribbon:
       NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    
  • 具体使用:
    @RestController
    @RequestMapping("/order")
    public class orderController {
    
    	//省略了获取元数据的步骤
    	//@Autowired
        //private DiscoveryClient discoveryClient;
        
        @Autowired
        private RestTemplate restTemplate;
    
        @RequestMapping(value = "/buy", produces = "text/plain;charset=UTF-8")
        @ResponseBody
        public String buy(int id) throws IOException {
        	//省略了获取元数据的步骤,在 restTemplate 进行访问的时候
        	//去注册中心寻找 microservice-provider-user 这个服务,然后进行负载均衡,返回一个正在的 IP 地址
            return this.restTemplate.getForObject("http://microservice-provider-user/product/"+id,String.class);
        }
    

5.3、重试

为了保证微服务的高可用,一般服务都是以集群的形式注册到注册中心,所以就需要 Ribbon 进行负载均衡。当消费者去调服务时, 通过 Ribbon 返回了一个具体的服务地址。当这个服务出现问题无法访问时,服务消费者会出现异常。为了解决这个问题,Ribbon 引入了重试机制,当消费者请求失败时,会根据配置进行重新发起请求。

  • 引入依赖:想要实现重试必须导入如下所示依赖,当引入这个依赖之后,就能进行重试了
    <dependency>
       <groupId>org.springframework.retry</groupId>
       <artifactId>spring-retry</artifactId>
    </dependency>
    
  • 添加重试配置:重试的配置 Ribbon 已经配置了,但是可以通过如下配置项覆盖默认的配置。
    #service-product5表示服务名,根据个人服务进行配置,没有固定值
    service-product5:
      ribbon:
        ConnectTimeout: 250 # Ribbon的连接超时时间
        ReadTimeout: 1000 # Ribbon的数据读取超时时间
        OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
        MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
        MaxAutoRetries: 1 # 对当前实例的重试次数
    

6、注解 @LoadBalanced

在使用负载均衡的过程中,我们就在启动类中添加了一个@LoadBalanced注解,这个注解主要用于给RestTemplate类添加一个 LoadBalancerClient接口的实现类,而这个类就是实现负载均衡的关键。

下面我们对整个流程进行解析:

  • 在启动项目时,会引入LoadBalancerAutoConfiguration配置类,这个类 Ribbon 中负载均衡的配置类。在这里插入图片描述
    我们重点关注下列这个方法:

    @Bean
    @ConditionalOnMissingBean
    public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
        return (restTemplate) -> {
            List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
            list.add(loadBalancerInterceptor);
            restTemplate.setInterceptors(list);
        };
    }
    

    上述方法中:

    • LoadBalancerInterceptor类,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
    • RestTemplateCustomizer类,用于给 RestTemplate 增加 LoadBalancerInterceptor 拦截器。
    • 通过调用 RestTemplateCustomizer 实例来给客户端负载均衡中被 @LoadBalanced注解修饰的 RestTemplate 对象列表增LoadBalancerInterceptor 拦截器。
  • LoadBalancerInterceptor拦截器:

    在这里插入图片描述
    在这个类中实例化了一个LoadBalancerClient接口的实现,LoadBalancerClient只是一个负载均衡器接口。

    当一个被 @LoadBalanced 注解修饰的 RestTemplate 对象向外发起HTTP请求时,会被LoadBalancerInterceptor 类的 intercept 函数所拦截。从 HttpRequest 的 URI 对象中通过 getHost() 就可以拿到服务名,然后根据服务名来选择实例并发起实际的请求。

  • BlockingLoadBalancerClientRibbonLoadBalancerClient都是接口的实现类

    在这里插入图片描述

7、Ribbon懒加载

Ribbon 在进行客户端负载均衡的时候并不是在启动的时候就加载,实在实际请求的时候才加载,这回导致第一次请求会比较的缓慢,甚至可能会出现超时的情况。所以我们可以指定具体的客户端名称来开启懒加载,即在启动的时候便加载素养的配置项的应用上下文。

具体服务名:
  eager-load:
    enabled: true
    clients: ribbon-client-a, ribbon-client-b, ribbon-client-c

版权声明:本文为qq_45040919原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。