【MSA】Spring Cloud Ribbon

发布时间 2023-06-20 23:14:31作者: lihewei

1. Ribbon 概述

​ Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 NetflixRibbon 实现。通过 Spring Cloud 的封装,可以让我们轻松地将面向服务的 REST 模版请求,自动转换成客户端负载均衡的服务调用。 轮询 hash 权重 ... 简单的说 Ribbon 就是 netfix 公司的一个开源项目,主要功能是提供客户端负载均衡算法和服务调用。Ribbon 客户端组件提供了一套完善的配置项,比如连接超时,重试等。在 Spring Cloud 构建的微服务系统中, Ribbon 作为服务消费者的负载均衡器,有两种使用方式,一种是和 RestTemplate 相结合,另一种是和 OpenFeign 相结合。OpenFeign 已经默认集成了 Ribbon。Ribbon 有很多子模块,但很多模块没有用于生产环境!

Ribbon 有两种使用方式:

  • 一种是和 RestTemplate 相结合
  • 另一种是和 OpenFeign 相结合

2. Ribbon 快速入门

项目搭建

consumer 和 provider-1 和 provider-2 都是 eureka-client

注意这三个依赖是:eureka-client

  • provider-1 和 provider-2 的 spring.application.name= provider

  • 启动类的注解和配置文件的端口以及服务名称,此处省略...

  1. pom依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        <version>2.2.9.RELEASE</version>
    </dependency>
    
  2. 编写 consumer 的启动类

    @SpringBootApplication
    @EnableEurekaClient
    public class ConsumerApplication {
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
      
        /**
         * 注册bean对象
         * RestTemplate是用来发请求的
         * @return
         */
        @Bean
      	@LoadBalanced //ribbon 的负载均衡注解
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
    
  3. 测试

    /**
    * 测试 ribbon 的负载均衡
    * @param serviceId
    * @return
    */
    @RequestMapping("/testRibbonBalance")
    public String testRibbonBalance(String serviceId) {
    	//直接用服务名称替换 ip:port
    	String url = "http://" + serviceId + "/info";
    	String forObject = restTemplate.getForObject(url,String.class);
    	System.out.println(forObject);
    	return forObject;
    }
    

3. Ribbon 源码分析

3.1 Ribbon 要做什么事情?

先通过 "http://" + serviceId + "/info" 我们思考 ribbon 在真正调用之前需要做什么?

restTemplate.getForObject(“http://provider/info”, String.class);

想要把上面这个请求执行成功,我们需要以下几步

  1. 拦截该请求;
  2. 获取该请求的 URL 地址:http://provider/info
  3. 截取 URL 地址中的 provider
  4. 从服务列表中找到 key 为 provider 的服务实例的集合(服务发现)
  5. 根据负载均衡算法选出一个符合的实例
  6. 拿到该实例的 host 和 port,重构原来 URL 中的 provider
  7. 真正发送的是 restTemplate.getForObject(“http://ip:port/info”,String.class)

3.2 Ribbon 负载均衡的实现和几种算法【重点】

策略类 名称 说明
RandomRule 随机策略 随机选择 Server
RoundRobinRule 轮训策略 按顺序循环选择 Server
RetryRule 重试策略 在一个配置时问段内当选择 Server 不成功,则一直尝试选择一个可用的 Server
BestAvailableRule 最低并发策略 逐个考察 Server,如果 Server 断路器打开,则忽略,再选择其中并发连接最低的 Server
AvailabilityFilteringRule 可用过滤策略 过滤掉一直连接失败并被标记为 circuit tripped 的 Server,过滤掉那些高并发连接的 Server(active connections 超过配置的网值)
ResponseTimeWeightedRule 响应时间加权策略 根据 Server 的响应时间分配权重。响应时间越长,权重越低,被选择到的概率就越低;响应时间越短,权重越高,被选择到的概率就越高。这个策略很贴切,综合了各种因素,如:网络、磁盘、IO等,这些因素直接影响着响应时间
ZoneAvoidanceRule 区域权衡策略 综合判断 Server 所在区域的性能和 Server 的可用性轮询选择 Server,并且判定一个 AWS Zone 的运行性能是否可用,剔除不可用的 Zone 中的所有 Server

4. Ribbon 配置文件设置

  • 修改 yml 配置文件(全局设置)

    ribbon: #全局的设置
    	eager-load:
    		enabled: false # ribbon 一启动不会主动去拉取服务列表,当实际使用时才去拉取 是否立即加载
    	http:
    		client:
    			enabled: false # 在 ribbon 最后要发起 Http 的调用调用,我们认为是RestTemplate 完成的,其实最后是 HttpURLConnection 来完成的,这里面设置为 true ,可以把 HttpUrlConnection->HttpClient
    	okhttp:
    		enabled: false #HttpUrlConnection 来完成的,这里面设置为 true ,可以把 HttpUrlConnection->OkHttpClient(也是发 http 请求的,它在移动端的开发用的多)
    
  • 超时时间设置

    ribbon: #feign 默认调用 1s 超时
    	ReadTimeout: 5000 #修改调用时长为 5s
    	ConnectTimeout: 5000 #修改连接时长为 5s
    

5. 项目中如何修改负载策略

5.1 使用配置类的方式

  1. 服务消费方A中创建配置类(注意不要放到@ComponentScan扫描的包或子包下),将指定的负载算法对象注入到容器中,此处指定随机

    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RandomRule;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MySelfRule {
      
        @Bean
        public IRule myRule(){
           /**
            * RandomRule随机负载,是IRule接口的实现类
            
            * 更改 负载均衡策略算法:
            * RandomRule #配置规则 随机
            * RoundRobinRule #配置规则 轮询
            * RetryRule #配置规则 重试
            * WeightedResponseTimeRule #配置规则 响应时间权重
            * 也可以自定义负载均衡策略的类
            * @return
            */
            return new RandomRule();
        }
    }
    
  2. 还是使用以前配置注入的 RestTemplate 对象,根据服务名称向注册中心获取服务调用列表

  3. 启动类添加 @RibbonClient 注解修饰,指定当前服务消费方调用哪个服务提供方时,使用的指定的负载策略

    import myrule.MySelfRule;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.ribbon.RibbonClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    //@EnableDiscoveryClient
    //指定服务提供方"CLOUD-PAYMENT-SERVICE",使用 MySelfRule 配置类中配置的负载策略
    @RibbonClient(name="CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain80.class,args);
        }
    }
    

    注意:在修改负载策略时,需要创建配置类,配置类不可以放在 @ComponentScan 扫描自动注入的当前包或子包路径下(@SpringBootApplication注解底层也是使用的 @ComponentScan),否则所有的 Ribbon 都会使用当前配置的这个负载策略

5.2 修改yaml配置方式

userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
    
#几种算法的全限定类名
  # 修改默认负载均衡算法,几种算法的全限定类名
  # NFLoadBalancerClassName: #loadBalance 策略
  # NFLoadBalancerPingClassName: #ping 机制策略
  # NIWSServerListClassName: #服务列表策略
  # NIWSServerListFilterClassName: #服务列表过滤策略
  # ZonePreferenceServerListFilter 默认是优先过滤非一个区的服务列表

5.3 RestTemplate 中更改负载均衡策略算法

RestTemplate 更改负载就均衡策略 只要在Ribbon配置类 RibbonConfig 添加代码即可,代码如下:

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * 如何将RestTemplate 和 Ribbon 相结合进行负载均衡?
 * 只需要在程序的IOC容器中注入一个 RestTemplate 的 Bean,并在这个 Bean 上加上 @LoadBalanced 注解(负载均衡注解)
 * 此时 RestTemplate 就结合 Ribbon 开启了负载均衡功能
 *
 */
@Configuration
public class RibbonConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    /**
     * 更改 负载均衡策略算法
     * RandomRule #配置规则 随机
     * RoundRobinRule #配置规则 轮询
     * RetryRule #配置规则 重试
     * WeightedResponseTimeRule #配置规则 响应时间权重
     * 也可以自定义负载均衡策略的类
     * @return
     */
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}