AbstractLoadBalance(负载均衡抽象类)
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {
if (CollectionUtils.isEmpty(invokers)) {
return null;
}
if (invokers.size() == 1) {
return invokers.get(0);
}
return doSelect(invokers, url, invocation);
}
protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);
所以下面的四种子类都继承与该抽象类,并实现doSelect方法
1、ConsistentHashLoadBalance(一致性哈希负载均衡)
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
//获取到方法名称
String methodName = RpcUtils.getMethodName(invocation);
//获取某一个服务方法的key(根据组+版本号+服务名称+方法名称)
String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
// using the hashcode of list to compute the hash only pay attention to the elements in the list
//获取当前list的hash值
int invokersHashCode = invokers.hashCode();
//根据key值获取对应的选择器
ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
//如果获取出来的选择器为空,
//或者选择器的hashCode不是当前list的hashCode(比如当前invokers有新的服务提供者上线,或者在原有的调用数量上下线了一台服务,都会影响到当前的hashcode变化),这个时候选择器就要从新初始化新的)
if (selector == null || selector.identityHashCode != invokersHashCode) {
selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, invokersHashCode));
selector = (ConsistentHashSelector<T>) selectors.get(key);
}
return selector.select(invocation);
}
为了方便理解上面的selectors,这里举一个例子
/**
* 服务方提供的接口,包含一个或多个方法
**/
public interface HelloService{
void hello();
void hi();
}
key1 : groupXXX:1.0.0:HelloService.hello
key2: groupXXX:1.0.0:HelloService.hi
这样在调用的时候,就会在selectors的map内就包含2个方法对应的key,并且会初始化两个key对应的一致性hash选择器
选择器初始化
ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
//初始化一个TreeMap(根据key的hashcode能够保证有序性)
this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
//把当前list的hash值存储下来,检查后期list是否有变动
this.identityHashCode = identityHashCode;
//获取服务提供url
URL url = invokers.get(0).getUrl();
//根据方法名称找到该方法的分片节点数量
this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);
String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));
argumentIndex = new int[index.length];
for (int i = 0; i < index.length; i++) {
argumentIndex[i] = Integer.parseInt(index[i]);
}
//遍历每个服务提供者,初始化TreeMap
for (Invoker<T> invoker : invokers) {
String address = invoker.getUrl().getAddress();
for (int i = 0; i < replicaNumber / 4; i++) {
byte[] digest = md5(address + i);
for (int h = 0; h < 4; h++) {
long m = hash(digest, h);
virtualInvokers.put(m, invoker);
}
}
}
}
/**
* 一致性hash选择服务提供者
**/
public Invoker<T> select(Invocation invocation) {
//拼接key
String key = toKey(invocation.getArguments());
//md5
byte[] digest = md5(key);
//选择
return selectForKey(hash(digest, 0));
}
private String toKey(Object[] args) {
StringBuilder buf = new StringBuilder();
for (int i : argumentIndex) {
if (i >= 0 && i < args.length) {
buf.append(args[i]);
}
}
return buf.toString();
}
private Invoker<T> selectForKey(long hash) {
//根据hash获取该hash值对应的key节点,或者大于当前hash值的下一个节点key
Map.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash);
if (entry == null) {
//如果为空,就把第一个节点给他
entry = virtualInvokers.firstEntry();
}
return entry.getValue();
}
LeastActiveLoadBalance(最少活跃调用负载均衡)
过滤最少活跃的节点,节点每处理一次active+1,过滤掉处理过多的节点,让处理少的节点来提供服务
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
// Number of invokers
//服务提供者数量
int length = invokers.size();
// The least active value of all invokers
//最少活跃值初始为-1
int leastActive = -1;
// The number of invokers having the same least active value (leastActive)
//拥有相同的活跃值的数量
int leastCount = 0;
// The index of invokers having the same least active value (leastActive)
//拥有节点活跃值相同,节点放入节点数组,供后期随机选择一个
int[] leastIndexes = new int[length];
// the weight of every invokers
//权重数组
int[] weights = new int[length];
// The sum of the warmup weights of all the least active invokers
//权重总和
int totalWeight = 0;
// The weight of the first least active invoker
//第一个权重值
int firstWeight = 0;
// Every least active invoker has the same weight value?
//权重是否相等
boolean sameWeight = true;
// Filter out all the least active invokers
for (int i = 0; i < length; i++) {
Invoker<T> invoker = invokers.get(i);
// Get the active number of the invoker
int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
// Get the weight of the invoker's configuration. The default value is 100.
//获取热身后正常的权重值
int afterWarmup = getWeight(invoker, invocation);
// save for later use
weights[i] = afterWarmup;
// If it is the first invoker or the active number of the invoker is less than the current least active number
//如果最少活跃值为-1说明第一次遍历,或者当前活跃值小于之前最少活跃值
if (leastActive == -1 || active < leastActive) {
// Reset the active number of the current invoker to the least active number
//将当前活跃值赋值
leastActive = active;
// Reset the number of least active invokers
//记录数量
leastCount = 1;
// Put the first least active invoker first in leastIndexes
//将当前节点角标保存
leastIndexes[0] = i;
// Reset totalWeight
//将当前节点的总权重记为总权重
totalWeight = afterWarmup;
// Record the weight the first least active invoker
firstWeight = afterWarmup;
// Each invoke has the same weight (only one invoker here)
sameWeight = true;
// If current invoker's active value equals with leaseActive, then accumulating.
}
//如果两个节点的活跃值相同,则将总权重进行累加,通过后期的随机权重算法进行选择
else if (active == leastActive) {
// Record the index of the least active invoker in leastIndexes order
//最少活跃的角标记录
leastIndexes[leastCount++] = i;
// Accumulate the total weight of the least active invoker
totalWeight += afterWarmup;
// If every invoker has the same weight?
if (sameWeight && afterWarmup != firstWeight) {
sameWeight = false;
}
}
}
// Choose an invoker from all the least active invokers
//发现当前只有一个最少活跃节点,则选择当前节点作为服务提供者
if (leastCount == 1) {
// If we got exactly one invoker having the least active value, return this invoker directly.
return invokers.get(leastIndexes[0]);
}
//如果权重不相同,需要调用随机权重
if (!sameWeight && totalWeight > 0) {
// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on
// totalWeight.
//生成一个总权重下面的一个随机整数
int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
// Return a invoker based on the random value.
for (int i = 0; i < leastCount; i++) {
int leastIndex = leastIndexes[i];
//生成的随机数减去当前节点的权重,如果最后的值小于0,则说明是当前节点
offsetWeight -= weights[leastIndex];
if (offsetWeight < 0) {
return invokers.get(leastIndex);
}
}
}
// If all invokers have the same weight value or totalWeight=0, return evenly.
//如果他们的权重相同,则随机从活跃节点数中选择一个节点,作为当前服务的调用节点
return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
}
RandomLoadBalance(随机负载均衡)
每个服务的节点权重可能设置不相同,需要从这些节点中选择一个节点作为服务的提供者
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
// Number of invokers
int length = invokers.size();
// Every invoker has the same weight?
boolean sameWeight = true;
// the weight of every invokers
int[] weights = new int[length];
// the first invoker's weight
int firstWeight = getWeight(invokers.get(0), invocation);
weights[0] = firstWeight;
// The sum of weights
int totalWeight = firstWeight;
for (int i = 1; i < length; i++) {
//获取当前节点的权重
int weight = getWeight(invokers.get(i), invocation);
// save for later use
weights[i] = weight;
// Sum
//进行累加
totalWeight += weight;
//如果权重不相同,则需要下面的根据权重随机选择
if (sameWeight && weight != firstWeight) {
sameWeight = false;
}
}
//如果总权重大于0,并且每个节点的权重不一样
if (totalWeight > 0 && !sameWeight) {
// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
//随机生成一个随机数(小于总权重)
int offset = ThreadLocalRandom.current().nextInt(totalWeight);
// Return a invoker based on the random value.
for (int i = 0; i < length; i++) {
//随机数减去当前权重,看是否小于0,如果小于,则选择当前节点,否则,下一个节点做减法
offset -= weights[i];
if (offset < 0) {
return invokers.get(i);
}
}
}
// If all invokers have the same weight value or totalWeight=0, return evenly.
//如果几个几点权重一样,则随机选择其中一个
return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
RoundRobinLoadBalance(轮询负载均衡)
轮询的方式进行提供,会考虑权重
//静态内部类 权重循环
protected static class WeightedRoundRobin {
private int weight;
private AtomicLong current = new AtomicLong(0);
private long lastUpdate;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
current.set(0);
}
public long increaseCurrent() {
return current.addAndGet(weight);
}
public void sel(int total) {
current.addAndGet(-1 * total);
}
public long getLastUpdate() {
return lastUpdate;
}
public void setLastUpdate(long lastUpdate) {
this.lastUpdate = lastUpdate;
}
}
private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();
/**
* get invoker addr list cached for specified invocation
* <p>
* <b>for unit test only</b>
*
* @param invokers
* @param invocation
* @return
*/
protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> invokers, Invocation invocation) {
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);
if (map != null) {
return map.keySet();
}
return null;
}
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
//生成key
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
//从map中获取key 对应的map,如果没有则生成新的map并返回
ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.computeIfAbsent(key, k -> new ConcurrentHashMap<>());
//总权重
int totalWeight = 0;
//最大值
long maxCurrent = Long.MIN_VALUE;
//当前时间戳
long now = System.currentTimeMillis();
Invoker<T> selectedInvoker = null;
WeightedRoundRobin selectedWRR = null;
//遍历服务提供者者
for (Invoker<T> invoker : invokers) {
String identifyString = invoker.getUrl().toIdentityString();
//获取当前提供者的权重
int weight = getWeight(invoker, invocation);
//根据key获取当前提供者对应的权重循环
WeightedRoundRobin weightedRoundRobin = map.computeIfAbsent(identifyString, k -> {
WeightedRoundRobin wrr = new WeightedRoundRobin();
wrr.setWeight(weight);
return wrr;
});
//如果提供者的权重被修改了,则更新weightedRoundRobin内的权重值
if (weight != weightedRoundRobin.getWeight()) {
//weight changed
weightedRoundRobin.setWeight(weight);
}
//current加上weight并获取结果
long cur = weightedRoundRobin.increaseCurrent();
weightedRoundRobin.setLastUpdate(now);
//判断是否比最大的值大
if (cur > maxCurrent) {
//如果大,则将当前服务提供者置为 本次服务提供者
maxCurrent = cur;
selectedInvoker = invoker;
selectedWRR = weightedRoundRobin;
}
//权重累计
totalWeight += weight;
}
//当两者大小不一致时,map中可能会存在一些已经下限的服务,本次剔除一些久节点信息
if (invokers.size() != map.size()) {
map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
}
//如果存在选择好的提供者,则改变他的current值 - totalWeight;
if (selectedInvoker != null) {
selectedWRR.sel(totalWeight);
return selectedInvoker;
}
// should not happen here
return invokers.get(0);
}