2.SpringCloud学习之-Ribbon学习

836 阅读4分钟

1.背景

今天我们学习SpringCloud的客户端负载均衡Ribbon

我们今天继续使用之前eureka-server作为服务注册中心

使用Springboot和springcloud的版本如下

  • springboot版本:2.3.5-release
  • springcloud版本:Hoxton.SR9

2.什么是Ribbon

Ribbon是一个开源的客户端负载均衡器,由Netflix发布,是SpringCloud-Netflix中重要的一环,通过它将Netflix的中间层服务连接在一起。 Ribbon客户端组件提供一系列完善的配置项,如连接超时、重试等。简单的说,就是在配置文件中列出Load Balancer后面所有的服务,Ribbon会自动的基于某种规则(如简单轮询,随机连接等)去连接这些服务,也很容易实现自定义的负载均衡算法。

什么是服务端负载均衡和客户端负载均衡

服务端负载均衡:

例如Nginx/H5,通过Nginx进行负载均衡,先发送请求,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配

客户端负载均衡:

例如spring cloud中的ribbon,客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡;即在客户端就进行负载均衡算法分配。

3.Ribbon的作用

Ribbon是在客户端来实现负载均衡的访问服务主要的功能点

  • 1服务发现发现依赖服务的列表
  • 2服务选择规则在多个服务中如何选择一个有效服务
  • 3服务监听检测失效的服务高效剔除失效服务

4.springcloud集成Ribbon(RestTemplate+Ribbon)

项目分为生产者和消费者

消费者分为3个步骤

添加依赖 里面包含ribbon的依赖

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

加注解 @EnableDiscoveryClient

加配置

@Configuration
public class Config {

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

生产分为3个步骤> 添加依赖 里面包含ribbon的依赖
	<dependencies>
	  <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>
	</dependencies>

加注解 @EnableDiscoveryClient

加配置

4.2 业务代码

消费者

@Data
public class User {

    private String id;

    private String name;
    private int age;

    private  int port;

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", port=" + port +
                '}';
    }
}

@RequestMapping("/api/comsumer/user")
@RestController
public class UserController {

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/{id}")
    public String selectUser(@PathVariable("id") String id){
        ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://ms-ribbon-producer/api/user/{id}",String.class,id);
        return responseEntity.getBody();
    }
}

生产者


@RequestMapping("/api/user")
@RestController
public class UserController {
    @Value("${server.port}")
    Integer port;

    @GetMapping("/{id}")
    public String selectUser(@PathVariable("id") String id){
        User user = new User();
        user.setId(id);
        user.setName("wangyunqi");
        user.setPort(port);
        return user.toString();
    }
}

验证

  1. 生产者:8002,8003端口
  2. 消费者:8001端口

代用消费者8001接口,发现返回的数据为8002,8003。轮训方式

5.Ribbon的负载均衡算法

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

6 如何修改默认的负载均衡算法

@Configuration
public class Config {

    //修改为随机方式
    @Bean
    public IRule rule(){
        return new RandomRule();
    }
}

6 如何自定义负载均衡算法

  1. 继承AbstractLoadBalancerRule
  2. 重写choose方法
/**
 * 自定义的随机策略
 * 
 */
public class CustomRandomRule extends AbstractLoadBalancerRule {

    Random rand;

    public CustomRandomRule() {
        rand = new Random();
    }

    private int currentIndex = 0;

    private List<Server> currentChooseList = new ArrayList<Server>();

    /**
     * Randomly choose from all living servers
     */
    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;
            }

            //第一次进来 随机选取一个下标
            int index = rand.nextInt(serverCount);

            //当前轮询的次数小于等于5
            if(currentIndex<5) {
                //保存当前选择的服务列表ip
                if(currentChooseList.isEmpty()) {
                    currentChooseList.add(upList.get(index));
                }
                //当前的++
                currentIndex++;
                //返回保存的
                return currentChooseList.get(0);
            }else {
                currentChooseList.clear();
                currentChooseList.add(0,upList.get(index));
                currentIndex=0;
            }


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

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

            server = null;
            Thread.yield();
        }

        return server;

    }

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

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

    }
}