Ribbon用于负载均衡(LB),底层是Netflix
LB分为集中式LB和进程内LB
集中式LB
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

初步配置Ribbon
修改microservicecloud-consumer-dept-80工程
pom文件修改

<!-- 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>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

yml文件修改

eureka:
  client:
    service-url:
      #      defaultZone: http://localhost:9001/eureka
      defaultZone: http://localhost:9001/eureka/,http://localhost:9002/eureka/,http://localhost:9003/eureka/
    register-with-eureka: false

对ConfigBean进行新注解@LoadBalanced

@Configuration
public class ConfigBean {

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

主启动类DeptConsumer_80添加@EnableEurekaClient

@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer_80 {

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

修改DeptController_Consumer客户端访问类

private final static String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";

最后启动集群以及服务,调用http://localhost/consumer/dept/list 等接口
出现结果则ribbon正常使用

为了测试ribbon的负载均衡效果
需要参考microservicecloud-provider-dept-8001搭建microservicecloud-provider-dept-8002,microservicecloud-provider-dept-8003,为8002和8003搭建各自数据库cloud2和cloud3
yml如下

server:
  port: 8002
mybatis:
  config-location: classpath:mybatis/mybatis.cfg.xml
  type-aliases-package: com.example.springcloud.bean
  mapper-locations: classpath:mybatis/mapper/*.xml
spring:
  application:
    name: microservicecloud-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud2
    username: root
    password: root
    dbcp2:
      min-idle: 5
      initial-size: 5
      max-total: 10

eureka:
  client:
    service-url:
#      defaultZone: http://localhost:9001/eureka
      defaultZone: http://localhost:9001/eureka/,http://localhost:9002/eureka/,http://localhost:9003/eureka/
  instance:
    instance-id: microservicecloud-provider8002
    prefer-ip-address: true

info:
    app.name: zy-example-microservicecloud
    company.name: zy_123
    build.artifactId: $project.artifactId$
    build.version: $project.version$
server:
  port: 8003
mybatis:
  config-location: classpath:mybatis/mybatis.cfg.xml
  type-aliases-package: com.example.springcloud.bean
  mapper-locations: classpath:mybatis/mapper/*.xml
spring:
  application:
    name: microservicecloud-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud3
    username: root
    password: root
    dbcp2:
      min-idle: 5
      initial-size: 5
      max-total: 10

eureka:
  client:
    service-url:
#      defaultZone: http://localhost:9001/eureka
      defaultZone: http://localhost:9001/eureka/,http://localhost:9002/eureka/,http://localhost:9003/eureka/
  instance:
    instance-id: microservicecloud-provider8003
    prefer-ip-address: true

info:
    app.name: zy-example-microservicecloud
    company.name: zy_123
    build.artifactId: $project.artifactId$
    build.version: $project.version$

启动集群,启动8001,8002,8003,启动consumer-80,调用http://localhost/consumer/dept/list,可以看到返回的数据库名字不同,默认使用ribbon是用的轮询的方式进行请求调用
Ribbon本身支持多种负载均衡的方式,核心组件为IRule
有一下类型的方式可供选择

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

下一步,自定义Ribbon规则
修改microservicecloud-consumer-dept-80
主启动类添加@RibbonClient
要注意官方文档明确给出了警告:
这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,
否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,也就是说
我们达不到特殊化定制的目的了。
所以新建package com.atguigu.myrule

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;

@Controller
public class MySelfRole {

    @Bean
    public IRule myRule(){
        return  new RandomRule();
    }
}

修改主启动类
@RibbonClient(name=“MICROSERVICECLOUD-DEPT”,configuration=MySelfRule.class)

启动全部服务,调用http://localhost/consumer/dept/list,即可看到为随机调用服务
修改RandomRule的源码

package com.example.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;

public class MyRandomRule extends AbstractLoadBalancerRule {
    private int total = 0;//总共被调用的次数,目前要求每台被调用5次
    private int currentIndex = 0;//当前提供服务的机器号

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

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

    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 = rand.nextInt(serverCount);
//            server = upList.get(index);
            // 改成每个服务器请求5次
            if (total < 5) {
                total++;
            } else {
                total = 0;
                currentIndex = ThreadLocalRandom.current().nextInt(serverCount);
                if (currentIndex >= upList.size()) {
                    currentIndex = 0;
                }
            }
            server = upList.get(currentIndex);


            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;

    }
}

修改myrule

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;

@Controller
public class MySelfRole {

    @Bean
    public IRule myRule(){
//        return  new RandomRule();
		return	new MyRandomRule();
    }
}

测试成功
至此,ribbon的基础使用到此为止。