if (lb == null) {
return null;
}
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 = chooseRandomInt(serverCount);
server = upList.get(index);
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
// 取随机数
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
}
重试策略 - RetryRule
================
先按照轮询负载策略获取服务实例,如果获取失败则在指定时间内(默认500ms)进行重试,循环调用轮询策略获取实例。
使用 InterruptTask 开启了一个 Timer 守护线程,用来延迟执行指定的任务,在重试时间范围内循环调用轮询策略获取服务器,如超过指定重试时间后仍未获取到服务器信息,则返回 null
public class RetryRule extends AbstractLoadBalancerRule {
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;
}
}
}
加权响应时间 - WeightedResponseTimeRule
=================================
WeightedResponseTimeRule 类继承了轮询策略类 RandomRule
初始化时,启动一个定时器,每隔 30s 根据服务的响应时间分配一次权重,响应时间越长,权重越低,被选择到的概率也越低。响应时间越短,权重越高,实例被选中概率越高。得到权重后,生成随机权重,命中权重比随机权重大的第一个服务实例。
public class WeightedResponseTimeRule extends RoundRobinRule {
// 每隔 30s 统计各服务权重
public static final int DEFAULT_TIMER_INTERVAL = 30 * 1000;
// 记录累计权重
private volatile List accumulatedWeights = new ArrayList();
// 初始化
void initialize(ILoadBalancer lb) {
if (serverWeightTimer != null) {
serverWeightTimer.cancel();
}
serverWeightTimer = new Timer(“NFLoadBalancer-serverWeightTimer-”
- name, true);
// 统计各服务权重
serverWeightTimer.schedule(new DynamicServerWeightTask(), 0,
serverWeightTaskTimerInterval);
// do a initial run
ServerWeight sw = new ServerWeight();
sw.maintainWeights();
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
logger
.info(“Stopping NFLoadBalancer-serverWeightTimer-”
- name);
serverWeightTimer.cancel();
}
}));
}
@Override
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
List currentWeights = accumulatedWeights;
// 判断线程是否中断
if (Thread.interrupted()) {
return null;
}
// 获取服务器列表
List allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
int serverIndex = 0;
// currentWeights.size() - 1 是所有权重的总和
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 {
// 从 0 到 所有权重总和之间获取随机数作为随机权重
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) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Next.
server = null;
}
return server;
}
}
// 内部类
class ServerWeight {
public void maintainWeights() {
ILoadBalancer lb = getLoadBalancer();
if (lb == null) {
return;
}
if (!serverWeightAssignmentInProgress.compareAndSet(false, true)) {
return;
}
try {
logger.info(“Weight adjusting job started”);
AbstractLoadBalancer nlb = (AbstractLoadBalancer) lb;
LoadBalancerStats stats = nlb.getLoadBalancerStats();
if (stats == null) {
// no statistics, nothing to do
return;
}
double totalResponseTime = 0;
// 计算出所有服务实例累计的平均响应时间
for (Server server : nlb.getAllServers()) {
ServerStats ss = stats.getSingleServerStat(server);
totalResponseTime += ss.getResponseTimeAvg();
}
// 记录累计权重
Double weightSoFar = 0.0;
// 存放所有服务的权重
List finalWeights = new ArrayList();
for (Server server : nlb.getAllServers()) {
ServerStats ss = stats.getSingleServerStat(server);
// 每个服务权重 = 所有服务的平均响应时间总和 - 当前服务的平均响应时间
// 所以服务的响应时间越大,权重越小,被选中的可能性越小
double weight = totalResponseTime - ss.getResponseTimeAvg();
weightSoFar += weight;
finalWeights.add(weightSoFar);
}
setWeights(finalWeights);
} catch (Exception e) {
logger.error(“Error calculating server weights”, e);
} finally {
serverWeightAssignmentInProgress.set(false);
}
}
}
例如:现在有三个服务实例,平均响应时间分别为:
-
A:100ms
-
B:200ms
-
C:300ms
则权重分别是:
-
A:600-100 = 500
-
B:500+600-200 = 900
-
C:900+600-300 = 1200
生成的随机数若在 0-500 之间,则命中服务 A,如在 500 - 900 之间,则命中服务 B,如在 900 - 1200,则命中服务 C,如果没有命中任何服务实例,则取轮询策略的结果。
最佳可用策略 - BestAvailableRule
==========================
如未指定负载均衡器,采用轮询策略选取一个服务实例;
如指定了负载均衡器,逐个考察服务实例,过滤掉断路器跳闸状态的实例,从未过滤掉的实例中选择一个并发量最小的实例。如果未命中,则轮询策略选取一个服务实例。
public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {
@Override
public Server choose(Object key) {
// 未指定负载均衡器,调用轮询策略
if (loadBalancerStats == null) {
return super.choose(key);
}
// 获取所有服务器列表
List 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;
}
}
}
可用性过滤策略 - AvailabilityFilteringRule
===================================
该策略继承自抽象策略 PredicateBasedRule 类。
通过轮询的方式选取一个服务,如果不匹配过滤条件,则继续轮询10次,如果10次还未命中,就轮询选取一个实例。
过滤条件:断路器故障或者并发请求超过了设置的并发阈值
public class AvailabilityFilteringRule extends PredicateBasedRule {
@Override
public Server choose(Object key) {
int count = 0;
// 轮询策略选一个实例
Server server = roundRobinRule.choose(key);
while (count++ <= 10) {
// 判断是否符合断言条件
if (predicate.apply(new PredicateKey(server))) {
return server;
}
// 不满足断言条件再轮询选择一个实例
server = roundRobinRule.choose(key);
}
// 超过10次还不满足,使用 父类 PredicateBasedRule
策略
return super.choose(key);
}
}
看看父类 PredicateBasedRule 的负载策略
public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
@Override
public Server choose(Object key) {
ILoadBalancer lb = getLoadBalancer();
// 根据条件过滤后,采用轮询策略选取实例
Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
最后
如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!
择一个实例
server = roundRobinRule.choose(key);
}
// 超过10次还不满足,使用 父类 PredicateBasedRule
策略
return super.choose(key);
}
}
看看父类 PredicateBasedRule 的负载策略
public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
@Override
public Server choose(Object key) {
ILoadBalancer lb = getLoadBalancer();
// 根据条件过滤后,采用轮询策略选取实例
Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
最后
如果觉得本文对你有帮助的话,不妨给我点个赞,关注一下吧!
[外链图片转存中…(img-ELihZbxG-1719186403838)]
[外链图片转存中…(img-9aovoMRy-1719186403839)]