Ribbon负载均衡

Ribbon是SpringCloud中的客户端负载均衡器,提供了多种负载策略如轮询、随机等。RoundRobinRule是最基础的轮询策略,通过循环选择服务器。客户端负载均衡与服务端(如Nginx)负载均衡不同,Ribbon在发送请求前选择服务器。自定义负载均衡时需避免配置类被多个客户端共享。文章还给出了RoundRobinRule的源码分析。
摘要由CSDN通过智能技术生成

1.介绍

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。

Ribbon是一个为客户端提供负载均衡功能的服务,它内部提供了一个叫做ILoadBalance的接口代表负载均衡器的操作,比如有添加服务器操作、选择服务器操作、获取所有的服务器列表、获取可用的服务器列表等等。

2.Ribbon默认自带的负载规则

请添加图片描述

  • RoundRobinRule 轮询
  • RandomRule 随机
  • RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重
  • WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
  • BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
  • ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器

3.Ribbon的负载均衡和Rest调用

Ribbon其实就是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和Eureka结合只是其中的一个实例。请添加图片描述
Ribbon在工作时分成两步:

1.先选择EurekaServer ,它优先选择在同一个区域内负载较少的server。
2.再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。

4.客户端负载均衡VS服务器端负载均衡

1_客户端端负载均衡
ribbon,客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这就是客户端负载均衡;即在客户端就进行负载均衡算法分配。请添加图片描述
2_服务端负载均衡
例如Nginx,通过Nginx进行负载均衡,先发送请求,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配。需要在nginx配置所有的服务提供者信息。
请添加图片描述

5.轮训规则的替换

⚠️这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。
也就是说不要将Ribbon配置类与主启动类同包)
在这里插入图片描述
在这里插入图片描述
自定义负载轮训,当然方法不止一种,可以看链接。在这里插入图片描述

在这里插入图片描述

6.源码

package com.netflix.loadbalancer;

import com.netflix.client.config.IClientConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * The most well known and basic load balancing strategy, i.e. Round Robin Rule.
 *
 * @author stonse
 * @author Nikos Michalakis <nikos@netflix.com>
 *
 */
public class RoundRobinRule extends AbstractLoadBalancerRule {
	
	// 定义了原子类的Integer,是线程安全的Integer
    private AtomicInteger nextServerCyclicCounter;
    // 定义了 服务存活的变量为真
    private static final boolean AVAILABLE_ONLY_SERVERS = true;
    // 定义了 所以服务的变量为假
    private static final boolean ALL_SERVERS = false;
    // 定义了 日志的对象
    private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);

	// 设置 默认的线程安全Integer为0
    public RoundRobinRule() {
        nextServerCyclicCounter = new AtomicInteger(0);
    }
	// 给lb设置负载均衡
    public RoundRobinRule(ILoadBalancer lb) {
        this();
        setLoadBalancer(lb);
    }

    //重点关注这方法。
    public Server choose(ILoadBalancer lb, Object key) {
    	// 如果负载均衡未开启为null 就提示没有负载均衡,并返回null
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }
		// 设定服务初始值为null
        Server server = null;
        // 计数数目初始为0
        int count = 0;
        // 注意下边是一个循环,我们循环获取需要提供服务的节点,最多失败10次
        while (server == null && count++ < 10) {
        	// 获取目前存活的服务主机(是真可用的服务节点,因为erueka有AP自我保护)
            List<Server> reachableServers = lb.getReachableServers();
            // 获取目前所有的服务主机
            List<Server> allServers = lb.getAllServers();
            // 设置当前存活的服务节点数量
            int upCount = reachableServers.size();
            // 设置所有的服务节点数量
            int serverCount = allServers.size();
            // 如果当前存活的主机和所有的主机都是0就提示没有可用的主机,并返回null
            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }
            // 我们调用了incrementAndGetModulo方法并传入了所有的服务节点数量
            int nextServerIndex = incrementAndGetModulo(serverCount);
            // 获取本次需要提供服务节点的索引
            server = allServers.get(nextServerIndex);
			//  如果本次需要提服务的节点如果为null,线程会进入让步状态
            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }
			// 如果需要提供服务的节点是可用状态,就使其进入准备状态并返回该服务节点
            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }
			// 设置服务节点变量为null
            // Next.
            server = null;
        }
		// 上边的循环已经进行了10次,还没有得到需要提供服务的节点,打印信息,返回null的服务节点对象了。
        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

    /**
     * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
     *
     * @param modulo The modulo to bound the value of the counter.
     * @return The next value.
     */
    private int incrementAndGetModulo(int modulo) {
  		// 这个方法并不是死循环,而是一个自旋锁
        for (;;) {
        	// 获取之前已经请求的次数 (我觉得应该是上次请求提供的服务节点索引更为合适,使用‘上次索引+1’和使用‘原始请求的次数+1’都不影响的最终结果,更推荐记录上次索引的,内存占用小)
            int current = nextServerCyclicCounter.get();
            // 将之前的请求次数+1,也就是这次请求的次数了。使用本次的次数对服务节点的数量进行取模运算,得到这次请求需要的节点索引next
            int next = (current + 1) % modulo;
            // 使用CAS 比较并交换.(如果我传入current和内存中的一样,我就用next的值与其进行交换,否则将继续这个自旋锁)
            if (nextServerCyclicCounter.compareAndSet(current, next))
           		// 返回本次需要提供服务的服务节点索引
                return next;
        }
    }

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

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}


7.参考网站

自定义负载均衡1🔗
自定义负载均衡2🔗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值