美文网首页
《五》、springcloud微服务——Ribbon负载均衡

《五》、springcloud微服务——Ribbon负载均衡

作者: 神奇作手 | 来源:发表于2019-07-20 18:32 被阅读0次

    Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端的 负载均衡的工具。
    Ribbon源码:https://github.com/Netflix/ribbon

    一、Ribbon的配置初步

    1、修改 microservicecloud-consumer-dept-80 工程

    (1)、修改pom.xml文件
       添加Ribbon 相关依赖

          <!-- Ribbon相关 -->
           <dependency>
               <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            </dependency>
    

    (2)、修改application.yml文件 追加eureka的服务注册地址

    server:
      port: 80
      
    eureka:
      client:
        register-with-eureka: false  #自己不能注册自己
        service-url: 
          defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
    

    (3)、对ConfigBean进行添加新注解@Load Balanced 获得Rest时加入Ribbon的配置

    @Configuration   //配置类,类似spring里面的applicationContext.xml
    public class ConfigBean {
    
        /**
         * RestTemplate提供了多种便捷访问远程Http服务的方法
          *     是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
          *    使用restTemplate访问restful接口非常的简单粗暴无脑。
         *  (url, requestMap, ResponseBean.class)这三个参数分别代表 
         *  REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
         */
        @Bean //注入bean
        @LoadBalanced //Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端,负载均衡的工具。 开启负载均衡
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    

    (4)、主启动类DeptConsumer80_App.java添加注解@EnableEurekaClient

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

    (5)、修改DeptController_Consumer.java客户端访问类

    package com.smilexl.springcloud.controller;
    
    import java.util.List;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    import com.smilexl.springcloud.entities.Dept;
    
    @RestController
    public class DeptController_Consumer {
        
        /**
         * RestTemplate提供了多种便捷访问远程Http服务的方法
          *   是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
          *   使用restTemplate访问restful接口非常的简单粗暴无脑。
         *  (url, requestMap, ResponseBean.class)这三个参数分别代表 
         *  REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
         */
        
    //  private static final String REST_URL_PREFIX = "http://localhost:8001";
        private static final String REST_URL_PREFIX = "http://SERVICESPRINGCLOUD-DEPT";  //改为微服务名字
    
        @Autowired
        private RestTemplate restTemplate;
    
        // 添加
        @GetMapping("/consumer/dept/add")
        public Boolean add(Dept dept) {
            return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
        }
    
        // 根据id查询
        @GetMapping("/consumer/dept/get/{id}")
        public Dept get(@PathVariable("id") Long id) {
            return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
        }
    
        // 根据id查询
        @SuppressWarnings("unchecked")
        @GetMapping("/consumer/dept/list")
        public List<Dept> list() {
            return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
        }
    }
    

    (6)、先启动3个eureka集群,再启动microservicecloud-provider-dept-8001并注册进eureka

    (7)、启动microservicecloud-consumer-dept-80,测试

    (8)、小总结

    Ribbon和Rureka整合后Cousumer可以直接调用服务而不用再关心地址和端口号


    二、Ribbon负载均衡

    1、架构说明

    Ribbon在工作时分成两步
      第一步先选择Eureka Server,它优先选择在同一个区域内负载较少的server,
      第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址,
      其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

    2、参考microservicecloud-provider-dept-8001,新建8002、8003
    • pom.xml文件中添加如下依赖
      <dependencies>
            <dependency><!-- 引入自己定义的api通用包,可以使用Dept部门Entity -->
                <groupId>cn.smilexl.springcloud</groupId>
                <artifactId>microservicecloud-api</artifactId>
                <version>${project.version}</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jetty</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
            
            <!-- 将微服务provider注册进eureka -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <!-- actuator监控信息完善 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
        </dependencies>
    
    3、新建8002、8003数据库,各微服务分别连自己的数据库
    • 8002 SQL脚本
    DROP DATABASE IF EXISTS cloudDB02;
    CREATE DATABASE cloudDB02 CHARACTER SET UTF8;
    USE cloudDB02;
    CREATE TABLE `dept` (
      `deptno` bigint(20) NOT NULL AUTO_INCREMENT,
      `dname` varchar(60) DEFAULT NULL,
      `db_source` varchar(60) DEFAULT NULL,
      PRIMARY KEY (`deptno`)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
    
    INSERT INTO `dept` VALUES ('1', '开发部', 'clouddb01');
    INSERT INTO `dept` VALUES ('2', '人事部', 'clouddb01');
    INSERT INTO `dept` VALUES ('3', '财务部', 'clouddb01');
    INSERT INTO `dept` VALUES ('4', '市场部', 'clouddb01');
    INSERT INTO `dept` VALUES ('5', '运维部', 'clouddb01');
    INSERT INTO `dept` VALUES ('6', '资讯部', 'clouddb01');
    
    • 8003 SQL脚本
    DROP DATABASE IF EXISTS cloudDB03;
    CREATE DATABASE cloudDB03 CHARACTER SET UTF8;
    USE cloudDB03;
    CREATE TABLE `dept` (
      `deptno` bigint(20) NOT NULL AUTO_INCREMENT,
      `dname` varchar(60) DEFAULT NULL,
      `db_source` varchar(60) DEFAULT NULL,
      PRIMARY KEY (`deptno`)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
    
    INSERT INTO `dept` VALUES ('1', '开发部', 'clouddb01');
    INSERT INTO `dept` VALUES ('2', '人事部', 'clouddb01');
    INSERT INTO `dept` VALUES ('3', '财务部', 'clouddb01');
    INSERT INTO `dept` VALUES ('4', '市场部', 'clouddb01');
    INSERT INTO `dept` VALUES ('5', '运维部', 'clouddb01');
    INSERT INTO `dept` VALUES ('6', '资讯部', 'clouddb01');
    
    4、修改8002、8003各子的yml
    • 8002 的yml
    server:
      port: 8002
      
    mybatis:
    #  config-location: classpath:mybatis/mybatis.cfg.xml       # mybatis配置文件所在路径
      type-aliases-package: cn.smilexl.springcloud.entities     # 所有Entity别名类所在包
      mapper-locations: classpath:mapper/**/*.xml               # mapper映射文件
        
    spring:
       application:
        name: microservicecloud-dept                           # 微服务名
       datasource:
        type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
        driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
        url: jdbc:mysql://47.99.218.123:3306/clouddb02          # 数据库名称
        username: root
        password: 123456
        dbcp2:
          min-idle: 5                                           # 数据库连接池的最小维持连接数
          initial-size: 5                                       # 初始化连接数
          max-total: 5                                          # 最大连接数
          max-wait-millis: 200                                  # 等待连接获取的最大超时时间
          
    eureka:
      client: #客户端注册进eureka服务列表内
        service-url: 
    #      defaultZone: http://localhost:7001/eureka
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
      instance:
        instance-id: microservicecloud-dept-8002  #自定义服务名称信息
        prefer-ip-address: true  #访问路径可以显示IP地址
     
    #点击超链接后显示信息   
    info:
      app.name: microservicecloud
      company.name: www.smilexl.cn
      build.artifactId: $project.artifactId$
      build.version: $project.version$
       
    
    • 8003 的yml
    server:
      port: 8003
      
    mybatis:
    #  config-location: classpath:mybatis/mybatis.cfg.xml       # mybatis配置文件所在路径
      type-aliases-package: cn.smilexl.springcloud.entities     # 所有Entity别名类所在包
      mapper-locations: classpath:mapper/**/*.xml               # mapper映射文件
        
    spring:
       application:
        name: microservicecloud-dept                           # 微服务名
       datasource:
        type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
        driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
        url: jdbc:mysql://47.99.218.123:3306/clouddb03          # 数据库名称
        username: root
        password: 123456
        dbcp2:
          min-idle: 5                                           # 数据库连接池的最小维持连接数
          initial-size: 5                                       # 初始化连接数
          max-total: 5                                          # 最大连接数
          max-wait-millis: 200                                  # 等待连接获取的最大超时时间
          
    eureka:
      client: #客户端注册进eureka服务列表内
        service-url: 
    #      defaultZone: http://localhost:7001/eureka
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
      instance:
        instance-id: microservicecloud-dept-8003  #自定义服务名称信息
        prefer-ip-address: true  #访问路径可以显示IP地址
     
     #点击超链接后显示信息   
    info:
      app.name: microservicecloud
      company.name: www.smilexl.cn
      build.artifactId: $project.artifactId$
      build.version: $project.version$
      
    

    注意:

    5、启动3个Dept微服务并且各自测试通过
      http://localhost:8001/dept/list
      http://localhost:8002/dept/list
      http://localhost:8003/dept/list
    
    6、启动 microservicecloud-consumer-dept-80
      http://localhost/consumer/dept/list
    

    客户端通过Ribbon完成负载均衡。注意观看返回的数据库名字,各不相同,负载均衡实现

    7、总结

       Ribbon其实就是一个负载均衡的客户端组件,他可以和其他所需要请求的客户端结合使用,和eureka结合只是其中的一个实例。

    三、Ribbon核心组件IRule

    IRule:根据特定算法中从服务列表中选取一个要访问的服务

    • RoundRobinRule:轮询;
    • RandomRule:随机;
    • AvailabilityFilteringRule:会过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
    • WeightedResponseTimeRule: 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时如果统计信息不足,则使用RoundTobinRule策略,等统计信息足够会切换到WeightedResponseTimeRule;
    • RetryRule: 先按照RoudRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用服务,如果还是失败的就会自动把这个失败的服务隔离掉;
    • BestAvailableRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量小的服务;
    • ZoneAvoidanceRule: 默认规则,复合判断server所在区域的性能和server的可用性选择服务器;

    修改Ribbon的算法:修改 microservicecloud-consumer-dept-80 中 /cfgbeans/ConfigBean.java文件

    @Configuration   //配置类,类似spring里面的applicationContext.xml
    public class ConfigBean {
        /**
         * RestTemplate提供了多种便捷访问远程Http服务的方法
          *     是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
          *    使用restTemplate访问restful接口非常的简单粗暴无脑。
         *  (url, requestMap, ResponseBean.class)这三个参数分别代表 
         *  REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
         */
        @Bean //注入bean
        @LoadBalanced //Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端,负载均衡的工具。 开启负载均衡
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
        
        @Bean
        public IRule myRule() {
            return new RandomRule();//达到的目的:用我们从新选择的随机算法代替默认的轮询算法
        }
        
    }
    //相对于,<bean id="restTemplate" class="com.smilexl.springcloud.cfgbean.RestTemplate">
        
    

    四、Ribbon 自定义算法

    1、在 microservicecloud-consumer-dept-80 主启动类上添加 @RibbonClient
    package cn.smilexl.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.ribbon.RibbonClient;
    import cn.smilexl.myrule.MyselfRule;
    
    @SpringBootApplication
    @EnableEurekaClient
    //在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效。
    @RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MyselfRule.class)
    public class DeptConsumer80_App {
        
        public static void main(String[] args) {
            SpringApplication.run(DeptConsumer80_App.class, args);
        }
    }
    

    @RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MyselfRule.class)

    • name:微服务的名字
    • configuration:自定义的算法类
      意思:对微服务 MICROSERVICECLOUD-DEPT,使用 MyselfRule.java类中的规则

    注意配置事项: 这个自定义类(MyselfRule.java)不能放在@ComponentScan(即:主启动类)所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。

    2、步骤

    (1)、新建package cn.smilexl.myrule 包

    (2)、在此包下新建自定义Ribbon规则类 MyselfRule.java

    package cn.smilexl.myrule;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RandomRule;
    
    @Configuration
    public class MyselfRule {
        
        @Bean
        public IRule myRule() {
            return new RandomRule();//Ribbon默认为轮询,自定义为随机
        }
    }
    
    3、Ribbon自定义规则深度解析

    需求:依旧轮询策略,但是加上新需求,每个服务器要求被调用5次。也即以前是每台机器一次,现在是每台机器5次。

    解析源码:https://github.com/Netflix/ribbon/blob/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer/RandomRule.java

    参考源码修改为需求要求的 RoundRobinRule_LL

    (1)、参考源码修改为需要的算法策略(RoundRobinRule_LL.java)

    package cn.smilexl.myrule;
    
    import java.util.List;
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.ILoadBalancer;
    import com.netflix.loadbalancer.Server;
    
    /**
     * 自定义需求规则类
     *  --依旧轮询策略,但是加上新需求,每个服务器要求被调用5次。也即以前是每台机器一次,现在是每台机器5次
     */
    
    public class RoundRobinRule_LL extends AbstractLoadBalancerRule {
    
        /**
         * Randomly choose from all living servers
         */
        
        private int total = 0; //总共被调用的次数,目前要求每台被调用5次
        private int currentIndex = 0; //当前提供服务的机器号
        
        @SuppressWarnings("unused")
        public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                return null;
            }
            Server server = null;
    
            while (server == null) {
                if (Thread.interrupted()) {
                    return null;
                }
                List<Server> upList = lb.getReachableServers();
                List<Server> allList = lb.getAllServers();
    
                int serverCount = allList.size();
                if (serverCount == 0) {
                    /*
                     * No servers. End regardless of pass, because subsequent passes
                     * only get more restrictive.
                     */
                    return null;
                }
    
    //            int index = chooseRandomInt(serverCount);
    //            server = upList.get(index);   
                //定义判断规则
                if(total < 5) {
                    server = upList.get(currentIndex); //获取当前的服务器
                    total++;
                }else {
                    total = 0;
                    currentIndex++;
                    if (currentIndex >= upList.size()) {//当前下标是否大于服务器总数
                        currentIndex = 0;
                    }
                }
    
                if (server == null) {
                    /*
                     * The only time this should happen is if the server list were
                     * somehow trimmed. This is a transient condition. Retry after
                     * yielding.
                     */
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive()) {
                    return (server);
                }
    
                // Shouldn't actually happen.. but must be transient or a bug.
                server = null;
                Thread.yield();
            }
    
            return server;
        }
    
        @Override
        public Server choose(Object key) {
            return choose(getLoadBalancer(), key);
        }
        
        @Override
        public void initWithNiwsConfig(IClientConfig arg0) {
            // TODO Auto-generated method stub
        }
    }
    

    (2)、在配置类中注入自定义的算法策略(MyselfRule.java)

    package cn.smilexl.myrule;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import com.netflix.loadbalancer.IRule;
    
    @Configuration
    public class MyselfRule {
        
        @Bean
        public IRule myRule() {
    //      return new RandomRule();//Ribbon默认为轮询,自定义为随机
    //      return new RoundRobinRule();//轮询
            return new RoundRobinRule_LL(); //调用自定义的算法规则,每台轮询调用5次
        }
    }
    

    (3)、最终工程目录结构如下:

    (4)、依次启动工程并测试结果

    依次启动工程:
    microservicecloud-eureka-7001;
    microservicecloud-eureka-7002;
    microservicecloud-eureka-7003;
    microservicecloud-provider-dept-8001;
    microservicecloud-provider-dept-8002;
    microservicecloud-provider-dept-8003;
    microservicecloud-consumer-dept-80;

    测试:localhost/consumer/dept/get/1

    相关文章

      网友评论

          本文标题:《五》、springcloud微服务——Ribbon负载均衡

          本文链接:https://www.haomeiwen.com/subject/qsiulctx.html