路由算法-WeightedResponseTimeRule
算法模拟:
1>假如现在有五台机器部署的同一个服务,30s内得这段时间统计得结果如下:
- 第一台: 120ms
- 第二台: 150ms
- 第三台: 80ms
- 第四台: 90ms
- 第五台: 180ms
那么路由总时间比重为: 120 + 150 + 80 + 90 + 180 = 620.0ms
2>每一台机器的比重如下:
初始化比重weight: 0.0
- 第一台: 0.0 + (620 - 120) = 500.0 weight:500.0 weightSoFar: 500.0
- 第二台: 500.0 + (620 - 150) = 970.0 weight:470.0 weightSoFar:970.0
- 第三台: 970.0 + (620 - 80) = 1510.0 weight: 540.0 weightSoFar:1510.0
- 第四台: 1510.0 + (620 - 90) = 2040.0 weight:530.0 weightSoFar:2040.0
- 第五台: 2040.0 + (620 - 180) = 2480.0 weight: 440.0 weightSoFar:2480.0
此时将每个weightSoFar放到一个列表中,与server列表相对应
3>概率计算如下:
- 第一台的概率: 500.0 / 2480.0 = 0.201612
- 第二台的概率: (970.0 - 500.0) / 2480.0 = 0.189516
- 第三台的概率: (1510.0 - 970.0) / 2480.0 = 0.217141
- 第四台的概率: (2040.0 - 1510.0) / 2480.0 = 0.213709
- 第五台的概率: (2480.0 - 2040.0) / 2480.0 = 0.177419
结果: 第三台 > 第四台 > 第一台 > 第二台 > 第五台
4>机器server的获取
(0.0~1.0)*2480获取的值落到(0,500]为第一台机器,落到(500,970]为第二台机器,落到(970,1510]为第三台机器,落到(1510,2040]为第四台机器,落到(2040,2480]为第五台机器,
定时任务
每30s获取一下权重列表
void initialize(ILoadBalancer lb) {
if (serverWeightTimer != null) {
serverWeightTimer.cancel();
}
//定时任务每间隔30s获得一下权重列表
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();
}
}));
}
class DynamicServerWeightTask extends TimerTask {
public void run() {
ServerWeight serverWeight = new ServerWeight();
try {
serverWeight.maintainWeights();
} catch (Exception e) {
logger.error("Error running DynamicServerWeightTask for {}", name, e);
}
}
}
权重列表的获取
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;
// 获得每台server平均响应时间的总和
for (Server server : nlb.getAllServers()) {
// this will automatically load the stats if not in cache
ServerStats ss = stats.getSingleServerStat(server);
totalResponseTime += ss.getResponseTimeAvg();
}
// weight for each server is (sum of responseTime of all servers - responseTime)
// so that the longer the response time, the less the weight and the less likely to be chosen
Double weightSoFar = 0.0;
// create new list and hot swap the reference
List<Double> finalWeights = new ArrayList<Double>();
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);
}
}
}
机器的获取
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
// get hold of the current reference in case it is changed from the other thread
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 {
// 取0.0-maxTotalWeight之间的随机数,然后再权重列表中获取区间内的server
double randomWeight = random.nextDouble() * maxTotalWeight;
// pick the server index based on the randomIndex
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;
}