前言:

本文内容:Ribbon:负载均衡及Ribbon、使用Ribbon实现负载均衡、自定义负载均衡策略

推荐免费SpringCloud基础教程视频:【狂神说Java】SpringCloud最新教程IDEA版_哔哩哔哩_bilibili

Ribbon:负载均衡及Ribbon

Ribbon是什么

  • Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具
  • Ribbon主要提供客户端软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon的客户端组件提供一系列完整的配置

Ribbon能干什么

  • LB,即负载均衡(Load Balance),在微服务或分布式集群中经常使用
  • 负载均衡简单说就是将用户的请求平均分摊到多个服务上,从而达到系统的HA(高可用)
  • 常见的负载均衡有Nginx、Lvs等
  • Dubbo和SpringCloud均为我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义
  • 负载均衡简单分类:
    • 集中式LB
      • 即在服务的消费方和提供方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务提供方
    • 进程式LB
      • 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后再从这些地址中选出一个合适的服务器
      • Ribbon属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取服务提供方地址

环境搭建

  1. 打开springcloud-consumer-dept-80的pom文件,引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    <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,添加配置

    1
    2
    3
    4
    5
    6
    # Eureka
    eureka:
    client:
    register-with-eureka: false
    service-url:
    defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002,http://eureka7003.com:7003
  3. 打开主启动类,添加注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.jokerdig.springcloud;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

    /**
    * @author Joker大雄
    * @data 2022/8/3 - 15:28
    **/
    @SpringBootApplication
    @EnableEurekaClient
    public class DeptConsumer_80 {
    public static void main(String[] args) {
    SpringApplication.run(DeptConsumer_80.class,args);
    }
    }
  4. config包下的ConfigBean中配置负载均衡

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.jokerdig.springcloud.config;

    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;

    /**
    * @author Joker大雄
    * @data 2022/8/3 - 15:14
    **/
    @Configuration
    public class ConfigBean {

    @Bean
    @LoadBalanced // Ribbon负载均衡
    public RestTemplate getRestTemplate(){
    return new RestTemplate();
    }
    }
  5. 修改controller包下的DeptConsumerController类中的内容

    1
    2
    3
    // private static final String REST_URL_PREFIX="http://localhost:8001";
    // Ribbon实现
    private static final String REST_URL_PREFIX="http://SPRINGCLOUD-PROVIDER-DEPT";
  6. 启动端口为7001,7002,7003,8001,80的模块,运行测试

    http://localhost/consumer/dept/list

    1
    [{"deptno":1,"dname":"开发部","db_source":"db01"},{"deptno":2,"dname":"生产部","db_source":"db01"},{"deptno":3,"dname":"研发部","db_source":"db01"},{"deptno":4,"dname":"人事部","db_source":"db01"},{"deptno":5,"dname":"运维部","db_source":"db01"}]
  7. 小结

    RibbonEureka整合后,客户端可以直接调用,不用关心IP地址和端口号

Ribbon:使用Ribbon实现负载均衡

创建多个服务提供者进行负载均衡

  1. 新建db02db03数据库

    db02

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    create database db02;

    use db02;

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;

    -- ----------------------------
    -- Table structure for dept
    -- ----------------------------
    DROP TABLE IF EXISTS `dept`;
    CREATE TABLE `dept` (
    `deptno` bigint(0) NOT NULL AUTO_INCREMENT,
    `dname` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
    `db_source` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
    PRIMARY KEY (`deptno`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '部门表' ROW_FORMAT = Dynamic;

    -- ----------------------------
    -- Records of dept
    -- ----------------------------
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('开发部', DATABASE());
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('生产部', DATABASE());
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('研发部', DATABASE());
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('人事部', DATABASE());
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('运维部', DATABASE());

    SET FOREIGN_KEY_CHECKS = 1;

    db03

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    create database db03;

    use db03;

    SET NAMES utf8mb4;
    SET FOREIGN_KEY_CHECKS = 0;

    -- ----------------------------
    -- Table structure for dept
    -- ----------------------------
    DROP TABLE IF EXISTS `dept`;
    CREATE TABLE `dept` (
    `deptno` bigint(0) NOT NULL AUTO_INCREMENT,
    `dname` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
    `db_source` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
    PRIMARY KEY (`deptno`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '部门表' ROW_FORMAT = Dynamic;

    -- ----------------------------
    -- Records of dept
    -- ----------------------------
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('开发部', DATABASE());
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('生产部', DATABASE());
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('研发部', DATABASE());
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('人事部', DATABASE());
    INSERT INTO `dept`(`dname`,`db_source`) VALUES ('运维部', DATABASE());

    SET FOREIGN_KEY_CHECKS = 1;
  2. 创建springcloud-provider-dept-8002(数据库db02)和springcloud-provider-dept-8003数据库db03)的Module,内容与springcloud-provider-dept-8002基本一致,除了数据库的变化

    博主8G内存同时运行4个项目已经占用已经满了,这部分推荐16G以上内存的电脑运行 orz

  3. 按顺序启动7001,7002,7003(可选),8001,8002,8003(可选),80等端口的项目/(ㄒoㄒ)/~~

  4. 运行测试

    http://localhost/consumer/dept/list

    每一次刷新访问,都会按照负载均衡的算法去访问最优的服务提供者

    1
    2
    3
    4
    5
    6
    7
    8
    // 每一次刷新访问
    [{"deptno":1,"dname":"开发部","db_source":"db01"},{"deptno":2,"dname":"生产部","db_source":"db01"},{"deptno":3,"dname":"研发部","db_source":"db01"},{"deptno":4,"dname":"人事部","db_source":"db01"},{"deptno":5,"dname":"运维部","db_source":"db01"}]

    // 每一次刷新访问
    [{"deptno":1,"dname":"开发部","db_source":"db03"},{"deptno":2,"dname":"生产部","db_source":"db03"},{"deptno":3,"dname":"研发部","db_source":"db03"},{"deptno":4,"dname":"人事部","db_source":"db03"},{"deptno":5,"dname":"运维部","db_source":"db03"}]

    // 每一次刷新访问
    [{"deptno":1,"dname":"开发部","db_source":"db02"},{"deptno":2,"dname":"生产部","db_source":"db02"},{"deptno":3,"dname":"研发部","db_source":"db02"},{"deptno":4,"dname":"人事部","db_source":"db02"},{"deptno":5,"dname":"运维部","db_source":"db02"}]

Ribbon:自定义负载均衡策略

  1. 打开springcloud-consumer-dept-80项目config包下的ConfigBean,配置官方的IRule

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    package com.jokerdig.springcloud.config;

    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RandomRule;
    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;

    /**
    * @author Joker大雄
    * @data 2022/8/3 - 15:14
    **/
    @Configuration
    public class ConfigBean {

    // 配置负载均衡实现RestTemplate
    // IRule
    // RoundRobinRule 轮询
    // RandomRule 随机
    // AvailabilityFilterIngRule:过滤掉故障服务
    // RetryRule 先按照轮询获取服务,如果获取失败,在指定时间内重试
    @Bean
    @LoadBalanced // 负载均衡
    public RestTemplate getRestTemplate(){
    return new RestTemplate();
    }

    // 随机 这是官方写好的
    // 自定义Rule的时候这个方法要注释掉
    @Bean
    public IRule myIRule(){
    return new RandomRule();
    }
    }
  2. 启动所有的Module进行测试

    http://localhost/consumer/dept/list

    每一次刷新,都会随机访问服务提供者

    1
    2
    3
    // 每一次刷新访问
    [{"deptno":1,"dname":"开发部","db_source":"db01"},{"deptno":2,"dname":"生产部","db_source":"db01"},{"deptno":3,"dname":"研发部","db_source":"db01"},{"deptno":4,"dname":"人事部","db_source":"db01"},{"deptno":5,"dname":"运维部","db_source":"db01"}]
    // .......省略
  3. 自定义IRule,新建包com.jokerdig.myrule,在包下新建JokerdigRuleJokerdigRandomRule

    这里包没有建在springcloud下而是它上一级的jokerdig下是为了防止被主启动类扫描到,导致自定义与原生的发生冲突

    JokerdigRule

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.jokerdig.myrule;

    import com.netflix.loadbalancer.IRule;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    /**
    * @author Joker大雄
    * @data 2022/8/5 - 13:03
    **/
    @Configuration
    public class JokerdigRule {


    // 自定义
    @Bean
    public IRule myIRule(){
    return new JokerdigRandomRule();
    }
    }

    JokerdigRandomRule

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    package com.jokerdig.myrule;

    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.ILoadBalancer;
    import com.netflix.loadbalancer.Server;

    import java.util.List;
    import java.util.concurrent.ThreadLocalRandom;

    public class JokerdigRandomRule extends AbstractLoadBalancerRule {

    // 自定义 这里所写内容仅供测试和了解自定义 并非实际使用
    private int total = 0; // 被调用次数
    private int currentIndex = 0; // 当前提供服务者


    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) {
    return null;
    }

    // nt index = chooseRandomInt(serverCount); // 生成区间随机数
    // server = upList.get(index); // 从存活的服务中随机获取一个
    // 自定义
    if(total<5){
    server = upList.get(currentIndex);
    total++;
    }else{
    total = 0;
    currentIndex++;
    if(currentIndex>upList.size()-1){
    currentIndex = 0;
    }
    server = upList.get(currentIndex);
    }


    if (server == null) {
    Thread.yield();
    continue;
    }

    if (server.isAlive()) {
    return (server);
    }

    server = null;
    Thread.yield();
    }

    return server;

    }

    protected int chooseRandomInt(int serverCount) {
    return ThreadLocalRandom.current().nextInt(serverCount);
    }

    @Override
    public Server choose(Object key) {
    return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    // TODO Auto-generated method stub

    }
    }
  4. 修改主启动类,添加@RibbonClient的注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package com.jokerdig.springcloud;

    import com.jokerdig.myrule.JokerdigRule;
    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;

    /**
    * @author Joker大雄
    * @data 2022/8/3 - 15:28
    **/
    @SpringBootApplication
    @EnableEurekaClient
    // 在微服务启动就能加载自定义的Ribbon类
    @RibbonClient(name = "SPRINGCLOUD-PROVIDER-DEPT",configuration = JokerdigRule.class)
    public class DeptConsumer_80 {
    public static void main(String[] args) {
    SpringApplication.run(DeptConsumer_80.class,args);
    }
    }
  5. 启动所有模块,进行测试

    http://localhost/consumer/dept/list

    每刷新到第5次,会更换所访问服务提供者