WeightedResponseTimeRule根据每个服务器的响应时间计算权重,根据这里得出的权重来选择服务器,相应时间越短的服务器被选择的概率越大。
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();
}
}));
}
在其initialize()方法中,构造一个定时任务,默认调用的时间间隔为30秒,调度的具体任务为DynamicServerWeightTask。其中的行为很简单,只是通过构造一个ServerWeight来调用maintainWeights()方法来重新计算各个服务器的权重,来方便需要负载均衡的时候根据最新计算得到的权重来达到目的。
class DynamicServerWeightTask extends TimerTask {
public void run() {
ServerWeight serverWeight = new ServerWeight();
try {
serverWeight.maintainWeights();
} catch (Throwable t) {
logger.error(
"Throwable caught while running DynamicServerWeightTask for "
+ name, t);
}
}
}
而在定时任务设置完毕之后,也会同时构造一个ServerWeight来调用maintainWeights()。
class ServerWeight {
public void maintainWeights() {
ILoadBalancer lb = getLoadBalancer();
if (lb == null) {
return;
}
if (serverWeightAssignmentInProgress.get()) {
return; // Ping in progress - nothing to do
} else {
serverWeightAssignmentInProgress.set(true);
}
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;
// find maximal 95% response time
for (Server server : nlb.getServerList(false)) {
// 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.getServerList(false)) {
ServerStats ss = stats.getSingleServerStat(server);
double weight = totalResponseTime - ss.getResponseTimeAvg();
weightSoFar += weight;
finalWeights.add(weightSoFar);
}
setWeights(finalWeights);
} catch (Throwable t) {
logger.error("Exception while dynamically calculating server weights", t);
} finally {
serverWeightAssignmentInProgress.set(false);
}
}
}
首先,保证权重计算的线程安全,也就是同一时间只能有一个正在计算权重的任务。
这里通过一个AtomicBoolean来作为信号量来保证本次任务在当前时间的唯一性。
接下来遍历所有的服务器列表,得到每一个服务器之前的平均答复时间,并依次相加,得到所有服务器平均响应时间的总和。
在得到时间的总和之后,之后会得到一个大小为服务器列表中服务器个数为大小的数组,首先再次遍历所有的服务器,通过之前的平均响应时间之和减去该服务器的平均响应时间作为权重,之后加上之前所有的权重之和,记录在之前生成的数组的相应位置。在遍历完所有服务器之后,将得到的数组保存下来,作为一次权重计算下来的结果。
在通过choose()方法得到相应的服务器的时候就相应的简单的多。
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);
在刚才的数组中,权重值得总和也就是数组中的最后一位,直接得到一个介于0到这个总和的一个随机数,在之前得到的权重数组得到第一个大于该随机数的位置的下标,也就是选择的服务器,返回。
由于相应时间越短的服务器权重越大,与前一位的差也越大,随机数命中的概率也越大。