【源码】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 权重 20
,B 权重 40
, C 权重 40
,则 accumulatedWeights
为 0, 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
CompositePredicate 是 AbstractServerPredicate 的实现类,支持多个 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
过滤 - CompositePredicate 的
apply
方法就是交由属性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();
}
ZoneAvoidanceRule 是 PredicateBasedRule 的实现类,维护属性 CompositePredicate compositePredicate
用于 服务实例 的过滤,该 CompositePredicate 由 ZoneAvoidancePredicate 和 AvailabilityPredicate 组合,前者判断 服务实例 的运行状况,过滤掉不可用的实例,后者过滤掉连接数过多的实例
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,委托 ZoneAvoidancePredicate 和 AvailabilityPredicate 组合的 CompositePredicate,对 服务实例 进行过滤,最后 轮询 返回
其中,AbstractServerPredicate CompositePredicate 等类的实现十分精巧,值得细细体味
下一章解读 Ribbon 与 OpenFeign、RestTemplate 的整合
上一篇:【源码】Spring Cloud —— Ribbon 2 ILoadBalancer
下一篇:【源码】Spring Cloud —— Ribbon 4 Ribbon 与 OpenFeign、RestTemplate 的整合