![](https://img.haomeiwen.com/i8553141/70093fa4ca66317b.png)
我们分别看一下关于RandomRule和RoundRobinRule类的源码
-
1.RandomRule:
![](https://img.haomeiwen.com/i8553141/d2665841aed5d37e.png)
![](https://img.haomeiwen.com/i8553141/379e3f94f43d99cb.png)
-
2.RoundRobinRule:
![](https://img.haomeiwen.com/i8553141/1bbf969cbf4b836f.png)
两者有两个区别:1.策略不一致,一个是轮训一个是随机 2.随机策略如果查询不到服务就会一直的查询,而轮训是有10次的限制,过了就不再查询
-
3.BestAvailableRule
![](https://img.haomeiwen.com/i8553141/fd041020b54b7d61.png)
如果当前这个负载均衡状态为空的话,就调用父类的选择,然后点进去看:
![](https://img.haomeiwen.com/i8553141/4ef7d45edf527dc7.png)
就会发现这个BestAvailableRule类是继承AbstractLoadBalancerRule,他的基础就是上一点钟的RoundRobinRule轮训的策略。
然后:
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;
}
}
}
获取所有的服务,然后获取当前时间,开始遍历所有的服务:
我们再看下这个代码:
ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
点进去看到这个代码:
private ServerStats getServerStats(Server server) {
try {
return serverStatsCache.get(server);
} catch (ExecutionException e) {
ServerStats stats = createServerStats(server);
serverStatsCache.asMap().putIfAbsent(server, stats);
return serverStatsCache.asMap().get(server);
}
}
会发现是否可以返回这个服务的缓存,如果有异常则初始化一个server的状态并返回。
private ServerStats createServerStats(Server server) {
ServerStats ss = new ServerStats(this);
//configure custom settings
ss.setBufferSize(1000);
ss.setPublishInterval(1000);
ss.initialize(server);
return ss;
}
最关键是这段代码:
serverStats.isCircuitBreakerTripped(currentTime)
再往下看:
public boolean isCircuitBreakerTripped(long currentTime) {
long circuitBreakerTimeout = getCircuitBreakerTimeout();
if (circuitBreakerTimeout <= 0) {
return false;
}
return circuitBreakerTimeout > currentTime;
}
private long getCircuitBreakerTimeout() {
long blackOutPeriod = getCircuitBreakerBlackoutPeriod();
if (blackOutPeriod <= 0) {
return 0;
}
return lastConnectionFailedTimestamp + blackOutPeriod;
}
private long getCircuitBreakerBlackoutPeriod() {
int failureCount = successiveConnectionFailureCount.get();
int threshold = connectionFailureThreshold.get();
if (failureCount < threshold) {
return 0;
}
int diff = (failureCount - threshold) > 16 ? 16 : (failureCount - threshold);
int blackOutSeconds = (1 << diff) * circuitTrippedTimeoutFactor.get();
if (blackOutSeconds > maxCircuitTrippedTimeout.get()) {
blackOutSeconds = maxCircuitTrippedTimeout.get();
}
return blackOutSeconds * 1000L;
}
获取到失败的链接,和失败的阈值,如果失败链接数小于阈值则直接返回0,否则计算一个差值,然后根据这个差值计算出一个熔断需要时间段,然后返回。
返回之后通过用 之前失败的时间+熔断需要时间段 得出一个最终熔断的时间。
然后就到这段代码:
if (!serverStats.isCircuitBreakerTripped(currentTime)) {
int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
if (concurrentConnections < minimalConcurrentConnections) {
minimalConcurrentConnections = concurrentConnections;
chosen = server;
}
}
这个判断意思就是如果没有到熔断时间的话我们就进入下面的逻辑:
获取这台服务当前的连接数,如果这个连接数小于minimalConcurrentConnections变量,则minimalConcurrentConnections变量赋值为该服务的连接数,然后chosen为当前服务,这段意思就是 遍历所有的服务,找到最小连接数的服务。
读到这里,我们就知道了,这个策略在轮训的策略基础上,找到服务器中最小连接数的服务。
-
4.RetryRule
IRule subRule = new RoundRobinRule();
public Server choose(ILoadBalancer lb, Object key) {
long requestTime = System.currentTimeMillis();
long deadline = requestTime + maxRetryMillis;
Server answer = null;
answer = subRule.choose(key);
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)) {
/* pause and retry hoping it's transient */
Thread.yield();
} else {
break;
}
}
task.cancel();
}
if ((answer == null) || (!answer.isAlive())) {
return null;
} else {
return answer;
}
}
从源码中我们可以看出RetryRule的实现是基于一个基础的策略,默认是RoundRobinRule,我们也可以修改成别的策略。
其实RetryRule就是在基础的策略上加了一个Retry的逻辑,跟着源码看,首先拿到最大的重试时间,然后计算出死亡时间,然后在死亡时间之前进行重试。
InterruptTask task = new InterruptTask(deadline
- System.currentTimeMillis());
这块的代码是在到了死亡时间就启动一个定时器,中断当前线程,而在死亡时间之前如果能获取到服务,则取消掉这个定时器。
-
5.WeightedResponseTimeRule
这个Rule继承自RoundRibbonRule,他会根据服务节点的响应时间计算权重,响应时间越长权重就越低,响应越快则权重越高,权重的高低决定了机器被选中概率的高低。也就是说,响应时间越小的机器,被选中的概率越大。
![](https://img.haomeiwen.com/i8553141/a84b1525bf253038.png)
由于服务器刚启动的时候,对各个服务节点采样不足,因此会采用轮询策略,当积累到一定的样本时候,会切换到WeightedResponseTimeRule模式。关于权重的计算方式,请大家参考源码阅读视频。
-
6.AvailabilityFilteringRule
这个规则底层依赖RoundRobinRule来选取节点,但并非来者不拒,它也是有一些底线的,必须要满足它的最低要求的节点才会被选中。如果节点满足了要求,无论其响应时间或者当前并发量是什么,都会被选中。
![](https://img.haomeiwen.com/i8553141/807ac812298b5cd8.png)
每次AvailabilityFilteringRule(简称AFR)都会请求RobinRule挑选一个节点,然后对这个节点做以下两步检查:
是否处于熔断状态(熔断是Hystrix中的知识点,后面章节会讲到,这里大家可以把熔断当做服务不可用)
节点当前的active请求连接数超过阈值,超过了则表示节点目前太忙,不适合接客
如果被选中的server不幸挂掉了检查,那么AFR会自动重试(次数最多10次),让RobinRule重新选择一个服务节点。
-
7.ZoneAvoidanceRule
这个过滤器包含了组合过滤条件,分别是Zone级别和可用性级别。
![](https://img.haomeiwen.com/i8553141/6b81931fa31c6ba5.png)
Zone Filter: 在Eureka注册中一个服务节点有Zone, Region和URL三个身份信息,其中Zone可以理解为机房大区(未指定则由Eureka给定默认值),而这里会对这个Zone的健康情况过滤其下面所有服务节点。
可用性过滤:这里和AvailabilityFilteringRule的验证非常像,会过滤掉当前并发量较大,或者处于熔断状态的服务节点。
网友评论