通过之前的源码解读,我们已经对Ribbon实现的负载均衡器以及其中包含的服务实例过滤器、服务实例信息的存储对象、区域的信息快照等都有了深入的认识和理解,但是对于负载均衡器中的服务实例选择策略只是讲解了几个默认实现的内容,而对于IRule的其他实现还没有详细的解读,下面我们来看看在Ribbon中共提供了那些负载均衡的策略实现。
![f8299d4f8c823bc2ea997bb71c61c245.png](https://img-blog.csdnimg.cn/img_convert/f8299d4f8c823bc2ea997bb71c61c245.png)
如上图所示,我们可以看到在Ribbon中实现了非常多的选择策略,其中也包含了我们在前面内容中提到过的:RoundRobinRule和ZoneAvoidanceRule。下面我们来详细的解读一下IRule接口的各个实现。
AbstractLoadBalancerRule
负载均衡策略的抽象类,在该抽象类中定义了负载均衡器ILoadBalancer对象,该对象能够在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息来作为分配依据,并以此设计一些算法来实现针对特定场景的高效策略。
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; }}
RandomRule
该策略实现了从服务实例清单中随机选择一个服务实例的功能。它的具体实现如下,可以看到IRule接口的choose(Object key)函数实现,委托给了该类中的choose(ILoadBalancer lb, Object key),该方法增加了一个负载均衡器对象的参数。从具体的实现上看,它会使用传入的负载均衡器来获得可用实例列表upList和所有实例列表allList,并通过rand.nextInt(serverCount)函数来获取一个随机数,并将该随机数作为upList的索引值来返回具体实例。同时,具体的选择逻辑在一个while (server == null)循环之内,而根据选择逻辑的实现,正常情况下每次选择都应该能够选出一个服务实例,如果出现死循环获取不到服务实例时,则很有可能存在并发的Bug。
@Overridepublic Server choose(Object key) {return choose(getLoadBalancer(), key);}public Server choose(ILoadBalancer lb, Object key) {...Server server = null;while (server == null) { if (Thread.interrupted()) { return null; } List upList = lb.getReachableServers(); List allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } int index = rand.nextInt(serverCount); server = upList.get(index);if (server == null) { Thread.yield(); continue; } if (server.isAlive()) { return (server); } server = null; Thread.yield(); } return server;}
RoundRobinRule
该策略实现了按照线性轮询的方式依次选择每个服务实例的功能。它的具体实现如下,其详细结构与RandomRule非常类似。除了循环条件不同外,就是从可用列表中获取所谓的逻辑不同。从循环条件中,我们可以看到增加了一个count计数变量,该变量会在每次循环之后累加,也就是说如果一直选择不到server超过10次,那么就会结束尝试,并打印一个警告信息No available alive servers after 10 tries from load balancer: ...。而线性轮询的实现则是通过AtomicInteger nextServerCyclicCounter对象实现,每次进行实例选择时通过调用incrementAndGetModulo函数实现递增。
public Server choose(ILoadBalancer lb, Object key) { ... Server server = null; int count = 0; while (server == null && count++ < 10) { List reachableServers = lb.getReachableServers(); List allServers = lb.getAllServers(); int upCount = reachableServers.size(); int serverCount = allServers.size(); if ((upCount == 0) || (serverCount == 0)) { log.warn("No up servers available from load balancer: " + lb); 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;}
RetryRule
该策略实现了一个具备重试机制的实例选择功能。从下面的实现中我们可以看到,在其内部还定义了一个IRule对象,默认使用了RoundRobinRule实例。而在choose方法中的则实现了对内部定义的策略进行反复尝试的策略,若期间能够选择到具体的服务实例就返回,若选择不到就根据设置的尝试结束时间为阈值(maxRetryMillis参数定义的值 + choose方法开始执行的时间戳),当超过该阈值后就返回null。