ribbon快速入门

Ribbon快速入门

简介:通过RestTemplate和Ribbon帮我们发起一个远程调用,同时完成负载均衡的任务

沿用上次Eureka服务器代码,创建一个新的空maven模块,里面新建三个springboot模块分别为consumer、provider-a、provider-b,由消费者通过ribbon调用提供者a或b的方法

三个springboot项目依赖还是使用springboot 2.3.12.RELEASE和springcloud Hoxton.SR12

  1. provider-a和provider-b配置文件和controlle代码,controller返回字符串区分到底调用哪个提供者的接口,配置文件只需要修改端口,其他保持一致,两个提供者是同一应用的不同实例,还有启动类需要添加@EnableEurekaClient注解。

    @RestController
    public class ProviderController {
    
        @GetMapping("hello")
        public String hello(){
            return "我是提供者aaa的接口";
        }
    }
    
    @RestController
    public class ProviderController {
    
        @GetMapping("hello")
        public String hello(){
            return "我是提供者bbb的接口";
        }
    }
    
    server:
      port: 8081
    
    spring:
      application:
        name: provider
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka
      instance:
        hostname: localhost
        prefer-ip-address: true
        instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    
    server:
      port: 8080
    
    spring:
      application:
        name: provider
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka
      instance:
        hostname: localhost
        prefer-ip-address: true
        instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    
  2. 消费者consumer配置文件、controller代码、启动类代码,RestTemplate起到发请求的作用,使用@LoadBalanced注解后,项目中restTemplate发起的请求都会走ribbon的代理

    @RestController
    public class ConsumerController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        /**
         * ribbon将http://provider/hello路径变成http://127.0.0.1:8080/hello
         * 1.拦截请求
         * 2.截取主机名称
         * 3.借助eureka来做服务发现list<>
         * 4.通过负载均衡算法 拿到一个服务ip port
         * 5.reConstructURL 重构url
         * 6.发起请求
         * @param serviceName
         * @return
         */
        @GetMapping("testRibbon")
        public String testRibbon(String serviceName){
            //需要拿到ip 端口和路径
            String result = restTemplate.getForObject("http://" + serviceName + "/hello", String.class);
            return result;
        }
    }
    
    server:
      port: 8082
    
    spring:
      application:
        name: consumer
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka
      instance:
        hostname: localhost
        prefer-ip-address: true
        instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}
    
    @SpringBootApplication
    @EnableEurekaClient
    public class ConsumerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ConsumerApplication.class, args);
        }
    
        /**
         * 这个RestTemplate已经变了
         * 加了@LoadBalanced注解会被ribbon来操作
         * @return
         */
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
  3. 启动上次的EurekaServer和本次三个springboot全部启动后,

    注意:最好先启动提供者,因为消费者启动后会先去注册中心拉去服务列表,所以如果先启动消费者,等一会再访问即可

    访问 localhost:8082/testRibbon?serviceName=provider

    这里就是访问消费者的controller,他会通过ribbon轮询访问提供者a和提供者b,刷新页面就会发现交替访问a或b

    ribbon快速入门_第1张图片

    ribbon快速入门_第2张图片

  4. 负载均衡算法:随机 轮询 权重 iphash

    轮询算法如何实现?

    int index = 请求次数 % size list.get(index)

    %取余好处是一个周期函数,让得到的结果总是小于除数。

    假设size=2,可以全局定义一个i,每次请求进来,i+1。1%2=1,2%2=0,3%2=1,4%2=0。这样每次交替访问0和1。但是int i =0 线程不安全,可能导致0 1被调用的次数有差异。

    怎么做线程安全的轮询算法?

    加锁效率低

    cas自旋锁 优点:java层面无锁的状态, 性能好 没有线程等待和唤醒的开销,缺点:会导致短暂时间内cpu飙升 还有ABA问题

  5. 轮询源码,可以通过loadBalancerClient.choose()找到源码chooseRoundRobinAfterFiltering方法,RoundRobin就是轮询的意思,eligible.size()也就是集合大小,再进incrementAndGetModulo方法

    /**
     * 核心是负载均衡
     * @param serviceName
     * @return
     */
    @GetMapping("testRibbonRule")
    public String testRibbonRule(String serviceName){
        //choose方法里面就是负载均衡算法
        ServiceInstance choose = loadBalancerClient.choose(serviceName);
        return choose.toString();
    }
    
    public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
        List<Server> eligible = this.getEligibleServers(servers, loadBalancerKey);
        return eligible.size() == 0 ? Optional.absent() : Optional.of(eligible.get(this.incrementAndGetModulo(eligible.size())));
    }
    
    private int incrementAndGetModulo(int modulo) {
        int current;
        int next;
        do {
            current = this.nextIndex.get();
            next = (current + 1) % modulo;
        } while(!this.nextIndex.compareAndSet(current, next) || current >= modulo);
    
        return current;
    }
    
  6. 如何修改负载均衡算法?

    • 修改配置文件,对每个服务设置负载均衡算法

      # 访问不同的服务可以使用不同的算法规则
      provider:   # 服务提供者的应用名
        ribbon:
          NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule  # 算法的全限定类名
      
    • 修改java代码,全局配置负载均衡算法

      /**
       * 全局配置负载均衡算法
       * 往容器中放一个rule对象
       * 访问任何提供者都是这个算法
       * @return
       */
      @Bean
      public IRule myRule(){
          return new RandomRule();
      }
      
  7. 其他配置项

    ribbon:
      eager-load:
        enabled: true   # ribbon需要去eureka获取服务列表 false是懒加载(默认false)
      eureka:
        enabled: true
      http: # 使用ribbon 用的restTemplate发请求(封装的)  java.net.HttpUrlConnection发请求(java原生 不支持连接池)
        client: # 发请求的工具很多 httpClient 支持连接池 效率高 如果想使用 改为true
          enabled: false
      okhttp: # 移动端使用比较多的请求工具 轻量级的请求 需要改为true
        enabled: false
    

你可能感兴趣的:(springcloud,ribbon,java,spring,cloud)