【源码】Spring Cloud —— Ribbon 3 IRule

前言

ILoadBalancer 封装了 Ribbon负载均衡 的实现,提供了诸如 服务实例获取、服务列表的维护 等功能,其中 服务实例 的获取最终由 负载均衡 策略实例 IRule 来实现,本章节解读 负载均衡 策略相关的接口 IRule 及其部分实现类

版本

Spring Cloud Netflix 版本:2.2.3.RELEASE

IRule

public interface IRule {

	// 选择服务实例
    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

顶层接口

AbstractLoadBalancerRule

public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {

    private ILoadBalancer lb;
        
    @Override
    public void setLoadBalancer(ILoadBalancer lb){
        this.lb = lb;
    }
    
    @Override
    public ILoadBalancer getLoadBalancer(){
        return lb;
    }      
}

抽象实现,持有 ILoadBalancer 对象

RandomRule

具体的实现类,RandomRule 会随机选择一个 服务实例

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

	public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
		
			// 从 lb 获取服务实例信息
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();

            int serverCount = allList.size();
            // 没有服务实例则返回 null
            if (serverCount == 0) {
                return null;
            }

			// 随机选择
            int index = chooseRandomInt(serverCount);
            server = upList.get(index);

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

			// 服务可用
            if (server.isAlive()) {
                return (server);
            }

            server = null;
            Thread.yield();
        }

        return server;

    }

RoundRobinRule

RoundRobinRule 的策略是 轮询

	public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }

        Server server = null;
        int count = 0;

		// 尝试 10 次
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                return null;
            }

			// 轮询
            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);

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

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

            server = null;
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

	--------------- incrementAndGetModulo ---------------

	// modulo:所有服务个数
	private int incrementAndGetModulo(int modulo) {
        for (;;) {
            int current = nextServerCyclicCounter.get();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }

轮询算法的实现也十分精巧,用递增的变量与服务总数取模,然后 CAS 操作更新计数器

WeightedResponseTimeRule

RoundRobinRule 的基础上,引入了 权重 的概念,权重由属性 List<Double> accumulatedWeights 维护,它是以这样的规则维护权重的:

一组递增的 Double 值,后者的 权重值 包含前者的 权重值,比如:A 权重 20B 权重 40C 权重 40,则 accumulatedWeights0, 20, 60, 100,因而只需要从左依次判断,选取第一个大于目标值的下标即可

权重取决于 服务响应时间,默认 30s 刷新一次

	public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            List<Double> currentWeights = accumulatedWeights;
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> allList = lb.getAllServers();

            int serverCount = allList.size();

            if (serverCount == 0) {
                return null;
            }

            int serverIndex = 0;

			// 最后一个值即总权重
            double maxTotalWeight = currentWeights.size() == 0 ? 0 : currentWeights.get(currentWeights.size() - 1); 

			// 权重值不合法或者权重数不合法,则直接轮询
            if (maxTotalWeight < 0.001d || serverCount != currentWeights.size()) {
                server =  super.choose(getLoadBalancer(), key);
                if(server == null) {
                    return server;
                }
            } else {

				// 生成随即目标数
                double randomWeight = random.nextDouble() * maxTotalWeight;
                // 选择第一个大于目标值的下标,返回对应 服务实例
                int n = 0;
                for (Double d : currentWeights) {
                    if (d >= randomWeight) {
                        serverIndex = n;
                        break;
                    } else {
                        n++;
                    }
                }

                server = allList.get(serverIndex);
            }

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

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

            server = null;
        }
        return server;
    }

RetryRule

重试策略,在 轮询 的基础上,加上默认 500ms 的重试时间

	public Server choose(ILoadBalancer lb, Object key) {
		long requestTime = System.currentTimeMillis();
		long deadline = requestTime + maxRetryMillis;

		Server answer = null;

		// IRule subRule = new RoundRobinRule(); 底层还是轮询
		answer = subRule.choose(key);

		// 重试直到 deadline 
		if (((answer == null) || (!answer.isAlive()))
				&& (System.currentTimeMillis() < deadline)) {

			InterruptTask task = new InterruptTask(deadline
					- System.currentTimeMillis());

			while (!Thread.interrupted()) {
				answer = subRule.choose(key);

				if (((answer == null) || (!answer.isAlive()))
						&& (System.currentTimeMillis() < deadline)) {
					Thread.yield();
				} else {
					break;
				}
			}

			task.cancel();
		}

		if ((answer == null) || (!answer.isAlive())) {
			return null;
		} else {
			return answer;
		}
	}

ClientConfigEnabledRoundRobinRule

包装了一层 RoundRobinRule,主要是交给子类拓展

public class ClientConfigEnabledRoundRobinRule extends AbstractLoadBalancerRule {

    RoundRobinRule roundRobinRule = new RoundRobinRule();

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        roundRobinRule = new RoundRobinRule();
    }

    @Override
    public void setLoadBalancer(ILoadBalancer lb) {
    	super.setLoadBalancer(lb);
    	roundRobinRule.setLoadBalancer(lb);
    }
    
    // 委托给 roundRobinRule,如果不设置则抛出异常
    @Override
    public Server choose(Object key) {
        if (roundRobinRule != null) {
            return roundRobinRule.choose(key);
        } else {
            throw new IllegalArgumentException(
                    "This class has not been initialized with the RoundRobinRule class");
        }
    }

}
BestAvailableRule

选择请求数最少的服务实例

	public Server choose(Object key) {
        if (loadBalancerStats == null) {
            return super.choose(key);
        }
        List<Server> serverList = getLoadBalancer().getAllServers();
        int minimalConcurrentConnections = Integer.MAX_VALUE;
        long currentTime = System.currentTimeMillis();
        Server chosen = null;

		// 遍历所有服务实例,选择请求数最少的一个
        for (Server server: serverList) {
            ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
            if (!serverStats.isCircuitBreakerTripped(currentTime)) {
                int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
                if (concurrentConnections < minimalConcurrentConnections) {
                    minimalConcurrentConnections = concurrentConnections;
                    chosen = server;
                }
            }
        }
        if (chosen == null) {
            return super.choose(key);
        } else {
            return chosen;
        }
    }
PredicateBasedRule

AbstractServerPredicate 过滤后,再进行轮询

public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
   
    public abstract AbstractServerPredicate getPredicate();
        
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();

		// 委托给 AbstractServerPredicate#chooseRoundRobinAfterFiltering
		// 即,由 AbstractServerPredicate 过滤后再轮询
        Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }       
    }
}

AbstractServerPredicate

过滤策略的核心抽象实现类

chooseRoundRobinAfterFiltering(List servers, Object loadBalancerKey)

该方法实现在过滤后的 服务实例 中轮询返回一个

	public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
		// 获取目标服务实例
        List<Server> eligible = getEligibleServers(servers, loadBalancerKey);
        if (eligible.size() == 0) {
            return Optional.absent();
        }

		// 轮询返回
        return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));
    }

getEligibleServers(List servers, Object loadBalancerKey)

	public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
        if (loadBalancerKey == null) {
            return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate()));            
        } else {
            List<Server> results = Lists.newArrayList();
            for (Server server: servers) {
                if (this.apply(new PredicateKey(loadBalancerKey, server))) {
                    results.add(server);
                }
            }
            return results;            
        }
    }

过滤 服务实例 并返回,过滤逻辑主要由 apply 方法执行

其他方法

AbstractServerPredicate 还提供了过滤后随即获取实例等方法,不再一一解读,这个类的设计十分优美,值得学习

CompositePredicate

CompositePredicateAbstractServerPredicate 的实现类,支持多个 AbstractServerPredicate 组合使用,复写了 getEligibleServers 方法

	public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
		// 此处调用的是父类的 getEligibleServers,即 apply 方法过滤
        List<Server> result = super.getEligibleServers(servers, loadBalancerKey);
        Iterator<AbstractServerPredicate> i = fallbacks.iterator();

		// 过滤完后如果服务数少于设置的阈值,则由 fallbacks 维护的过滤器过滤
		// 直到满足预期或者 fallbacks 过滤器用完
        while (!(result.size() >= minimalFilteredServers && result.size() > (int) (servers.size() * minimalFilteredPercentage))
                && i.hasNext()) {
            AbstractServerPredicate predicate = i.next();
            result = predicate.getEligibleServers(servers, loadBalancerKey);
        }
        return result;
    }
  • 先调用父类的 getEligibleServers 方法,即执行 apply 过滤
  • CompositePredicateapply 方法就是交由属性 AbstractServerPredicate delegate 执行
  • 如果过滤后 服务实例 少于设置的 阈值(即过滤力度过大了),那么改由 fallbacks 维护的过滤器过滤

ZoneAvoidanceRule

核心实现类,也是 Spring Cloud 默认提供的 IRule 组件类

	// 用于过滤服务的 CompositePredicate 
	private CompositePredicate compositePredicate;
    
    public ZoneAvoidanceRule() {
        super();
        ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);
        AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);
        compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
    }
    
    // 该 CompositePredicate 由 ZoneAvoidancePredicate 和 AvailabilityPredicate 组合
    private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {
    	// delegate: p1 和 p2
    	// fallbacks: p2 和 AbstractServerPredicate.alwaysTrue()
    	// 也就是说如果 p1 和 p2 联合过滤后服务实例不够,则由 p2 单独过滤
    	// 		如果仍然不够,则全部通过
        return CompositePredicate.withPredicates(p1, p2)
                             .addFallbackPredicate(p2)
                             .addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
                             .build();
        
    }

ZoneAvoidanceRulePredicateBasedRule 的实现类,维护属性 CompositePredicate compositePredicate 用于 服务实例 的过滤,该 CompositePredicateZoneAvoidancePredicateAvailabilityPredicate 组合,前者判断 服务实例 的运行状况,过滤掉不可用的实例,后者过滤掉连接数过多的实例

ZoneAvoidancePredicate#apply

	public boolean apply(@Nullable PredicateKey input) {
        if (!ENABLED.get()) {
            return true;
        }
        String serverZone = input.getServer().getZone();
        // 如果目标实例没有区域属性,则返回 true
        if (serverZone == null) {
            return true;
        }
        LoadBalancerStats lbStats = getLBStats();
        if (lbStats == null) {
            return true;
        }
        // 可用区域 <= 1,返回 true
        if (lbStats.getAvailableZones().size() <= 1) {
            return true;
        }

		// 借助 区域快照 和 平均负载属性 进行过滤
        Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
        if (!zoneSnapshot.keySet().contains(serverZone)) {
            return true;
        }
        logger.debug("Zone snapshots: {}", zoneSnapshot);
        Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
        logger.debug("Available zones: {}", availableZones);

		// 是否过滤取决于过滤后的 服务实例 列表是否包含当前 服务实例
        if (availableZones != null) {
            return availableZones.contains(input.getServer().getZone());
        } else {
        	// 如果过滤后的可用区为 null,则返回 false:目标服务实例不匹配
            return false;
        }
    }  

区域快照平均负载属性 过滤出所有可用区域,如果并不包含当前区域,则过滤掉

AvailabilityPredicate#apply

public boolean apply(@Nullable PredicateKey input) {
        LoadBalancerStats stats = getLBStats();
        if (stats == null) {
            return true;
        }
        return !shouldSkipServer(stats.getSingleServerStat(input.getServer()));
    }
   
    private boolean shouldSkipServer(ServerStats stats) { 
    	// 当前服务断路或连接数超过阈值,则返回 true,即目标实例不匹配      
        if ((CIRCUIT_BREAKER_FILTERING.get() && stats.isCircuitBreakerTripped()) 
                || stats.getActiveRequestsCount() >= activeConnectionsLimit.get()) {
            return true;
        }
        return false;
    }

判断逻辑在方法 shouldSkipServer 中,如果服务断路或者连接数 大于等于 设置的阈值,则认为连接数过多,被过滤掉

总结

该章节主要解读了 IRule 接口及其实现类,其中也提到了 AbstractServerPredicate 分支,因为 Spring Cloud 默认提供的 IRule 组件类 ZoneAvoidanceRule,委托 ZoneAvoidancePredicateAvailabilityPredicate 组合的 CompositePredicate,对 服务实例 进行过滤,最后 轮询 返回

其中,AbstractServerPredicate CompositePredicate 等类的实现十分精巧,值得细细体味

下一章解读 RibbonOpenFeignRestTemplate 的整合

上一篇:【源码】Spring Cloud —— Ribbon 2 ILoadBalancer

下一篇:【源码】Spring Cloud —— Ribbon 4 Ribbon 与 OpenFeign、RestTemplate 的整合

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值