zoukankan      html  css  js  c++  java
  • Spring Cloud 之 ribbon

    参考:<<重新定义springcloud>>

      各个视频

    什么是ribbon?它解决了什么问题

    • Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,他是基于Netflix Ribbon实现的。
    • 它不像spring cloud服务注册中心,配置中心,API网关那样独立部署,但是它几乎在每一个spring cloud微服务中,包括feign提供的声明式服务调用也是基于Ribbon实现的。
    • ribbon提供了很多种负载均衡算法,例如 轮询、随机 等等。甚至是包含自定义的负载均衡算法。
    • 他解决了微服务的负载均衡的问题

    负载均衡解决方案的分类

    目前业界主流的负载均衡方案课分为俩类:

    1、集中式负载均衡,就是在consumer和provider之间使用独立的负载均衡设施(可以是硬件,如F5,也可以是软件 ,nginx)由这些设施 负责把访问请求 通过 某种策略转发至provider

    2、进程内负载均衡,将负载均衡逻辑集成到consumer,consumer从注册中心获取有哪些服务地址,从中通过某种策略挑选出一个provider

    Ribbon 就是属于后者,他只是一个类库,集成于consumer进程,consumer通过他来获取到provider的地址

    他们的区别架构图

     Ribbon的负载均衡算法策略

    id 命名 策略类 描述
    1 轮询算法 RoundRibbonRule 按顺序循环选择server
    2 随机算法 randomRule 随机选择server
    3 重试策略 retryRule 在一个配置时间内当选择的server不成功,则一直尝试选择一个可用的server
    4 最低并发策略 BestAvailableRule 逐个考擦server,如果server断路器打开,则忽略,选择请求中并发最小的server,
     5  可用过滤策略  availabilityFilteringRule  过滤掉一直连接失败并被标记为ciruruit tripped的server,过滤掉哪些高并发连接的server(active connections超过配置的阈值)
     响应时间加权策略  responseTimeWeightRule  根据server的响应时间分配权重,响应时间越长,权重越低,响应时间越短,权重越高,被选中的概率就越高,这个策略很贴切,综合了。网络 磁盘 IO等因素
     7  区域权衡策略  zoneavoidanceRule  

     综合判断server所在区域的性能和server的可用性轮询选择server,判断一个区域是否可用,如果不可用直接将这个区域的sever都丢弃

    如:多机房情况

    使用RIbbon实现的入门实例:

    1.先创建一个父工程:只有pom文件 hello-springcloud

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.xiaodao</groupId>
        <artifactId>hello-springcloud</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
        <modules>
            <module>eureka_server</module>
            <module>client-a</module>
            <module>client-a</module>
            <module>ribbon-client</module>
        </modules>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.0.RELEASE</version>
        </parent>
        <!--  利用传递依赖,公共部分  -->
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!-- springboot web -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    
        <!-- 管理依赖 -->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Finchley.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    View Code

    2.创建一个eurekaserver服务:client-a

    pom文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>hello-springcloud</artifactId>
            <groupId>com.xiaodao</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>eureka_server</artifactId>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    View Code

    bootstrap.yml 和启动类

    server:
      port: 8888
    eureka:
      instance:
        hostname: localhost
      client:
        registerWithEureka: false
        fetchRegistry: false
        serviceUrl:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/


    /**
    * Created by xiaodao
    * date: 2019/7/17
    */
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServerApplication {

    public static void main(String[] args) {
    SpringApplication.run(EurekaServerApplication.class,args);
    }
    }
     

    3.创建一个provider 

       <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
        </dependencies>

    启动类:

    @SpringBootApplication
    @EnableDiscoveryClient
    public class ClientAApplication {
        
        public static void main(String[] args) {
            SpringApplication.run(ClientAApplication.class, args);
        }
    }

    controller:

    package com.xiaodao.controller;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
    
        @GetMapping("/add")
        public String add(Integer a, Integer b, HttpServletRequest request){
            return " From Port: "+ request.getServerPort() + ", Result: " + (a + b);
        }
    }
    View Code

    3.创建第3个项目:ribbon-client

        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
        </dependencies>

    bootstrap.yml

    spring:
      application:
        name: ribbon-loadbalancer
    server:
      port: 7777
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8888/eureka/
    #默认是hostname注册,改成ip注册
    instance: prefer-ip-address: true

    启动类:

    @SpringBootApplication
    @EnableDiscoveryClient
    public class RibbonLoadbalancerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RibbonLoadbalancerApplication.class, args);
        }
        
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }

    testController:

    @RestController
    public class TestController {
        
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("/add")
        public String add(Integer a, Integer b) {
            String result = restTemplate
                    .getForObject("http://CLIENT-A/add?a=" + a + "&b=" + b, String.class);
            System.out.println(result);
            return result;
        }
    }

    现在我们的项目都创建完成了.我们启动俩个client-a不过端口不一样.启动ribbon-client 启动 hello-springcloud

    当我们请求localhost:7777/add?a=1&b=10 的时候我们可以看到打印的日志:

     From Port: 7070, Result: 11
     From Port: 7071, Result: 11
     From Port: 7070, Result: 11
     From Port: 7071, Result: 11
     From Port: 7070, Result: 11
     From Port: 7071, Result: 11
     From Port: 7070, Result: 11

    换一个中策略来实现

    前面提到过那么多策略模式.我们模式的是轮询模式,现在来挑选一个最简单的可以看出来的随机

    我们有配置文件和java俩种配置

       @Bean
        public IRule ribbonRule() {
            return new RandomRule();//这里配置策略,和配置文件对应
        }

     配置文件方式:

    spring:
      application:
        name: ribbon-loadbalancer
    server:
      port: 7777
    eureka:
      client:
        serviceUrl:
          defaultZone: http://localhost:8888/eureka/
    
      instance:
        prefer-ip-address: true
    CLIENT-A:
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

    点对点直连,让ribbon调试更加方便快捷

    什么是点对点直连?

    cusumer和provider点对点直连不经过注册中心,为什么呢?因为开发环境都是有eureka来做注册中心,开发中大家的idea都是启动的,你要和对方进行联调,很难联调

    如果我和张三联调.负载均衡经常发给李四和王五,这种情况没法联调.有人说我要和张三联调,让李四和王五先停下来.这怎么行?

    如何实现呢?

    1.在consumer就是你要调用对方的自己的服务:

    将uereka移除

      <!--  <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>-->
    @SpringBootApplication
    //@EnableDiscoveryClient
    public class RibbonLoadbalancerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RibbonLoadbalancerApplication.class, args);
        }
        
      @Bean
    //    @LoadBalanced
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
        /*@Bean
        public IRule ribbonRule() {
            return new RandomRule();//这里配置策略,和配置文件对应
        }*/
    }

    pom文件:

    spring:
      application:
        name: ribbon-loadbalancer
    server:
      port: 8082
    #eureka:
    #  client:
    #    serviceUrl:
    #      defaultZone: http://localhost:8888/eureka/
    #
    #  instance:
    #    prefer-ip-address: true
    #禁用eureka
    ribbon:
      eureka:
        enabled: false
    CLIENT-A:
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
        listOfServers: http://127.0.0.1:7070,http://127.0.0.1:7071

    在调用的时候需要这么写:

    @RestController
    public class TestController {
        
        @Autowired
        private RestTemplate restTemplate;
        @Autowired
        private LoadBalancerClient loadBalancerClient;//ribbon 负载均衡客户端
    
        @GetMapping("/add")
        public String add(Integer a, Integer b) {
            ServiceInstance si=loadBalancerClient.choose("CLIENT-A");
            StringBuffer sb=new StringBuffer("");
            sb.append("http://");
            sb.append(si.getHost());
            sb.append(":");
            sb.append(si.getPort());
            sb.append("/add?a="+a+"&b="+b);
            System.out.println(sb.toString());
            String result = restTemplate.getForObject(sb.toString(), String.class);
    //        String result = restTemplate
    //                .getForObject("http://CLIENT-A/add?a=" + a + "&b=" + b, String.class);
            System.out.println(result);
            return result;
        }
    }

    这个时候,就可以实现点对点的联调.

    关于@loadBanced的实现.

    就是基于实际是通过实现 ClientHttpRequestInterceptor 实现的

    LoadBalancerInterceptor
    LoadBalancerClient
    RibbonLoadBalancerClient

    然后经过LoadBalancerAutoConfiguration自动配置将loadbalanceInterceptor变为resttemplate的的拦截器

    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {
    
       @Bean
       public LoadBalancerInterceptor ribbonInterceptor(
             LoadBalancerClient loadBalancerClient,
             LoadBalancerRequestFactory requestFactory) {
          return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
       }
    
       @Bean
       @ConditionalOnMissingBean
       public RestTemplateCustomizer restTemplateCustomizer(
             final LoadBalancerInterceptor loadBalancerInterceptor) {
          return restTemplate -> {
             List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                   restTemplate.getInterceptors());
             list.add(loadBalancerInterceptor);
             restTemplate.setInterceptors(list);
          };
       }
    
    }

    这里不做多讲.毕竟在下也是只知皮毛.

  • 相关阅读:
    eclipse安装svn插件,在输入url后,一直卡在in progress界面不懂。
    android——背景颜色渐变(梯度变化)
    android——ObjectAnimator动画(一)
    android——ObjectAnimator动画
    android 无线调试
    android——字体颜色跟随状态改变
    爬虫基本原理
    java基础 数组
    java基础 方法
    java基础 流程控制和条件语句,循环语句
  • 原文地址:https://www.cnblogs.com/bj-xiaodao/p/11198160.html
Copyright © 2011-2022 走看看