Dubbo's load balancing strategy algorithm
Dubbo provides four kinds of balancing strategies in cluster load balancing, the default is RandomLoadBalance
/**
* Random LoadBalance
* Random, set the random probability according to the weight
* The probability of collision on a section is high, but the larger the number of calls, the more uniform the distribution , and the weights are relatively uniform after using the weights according to the probability, which is conducive to dynamically adjusting the provider weights.
* @author junlee 2016-08-07
* @Email: [email protected]
* @version
*/
public class RandomLoadBalance extends AbstractLoadBalance {
public static final String NAME = "random";
private final Random random = new Random();
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size(); // total number
int totalWeight = 0; // total weight
boolean sameWeight = true; // whether the weights are all the same
for (int i = 0; i < length; i++) {
int weight = getWeight(invokers.get(i), invocation);
totalWeight += weight; // cumulative total weight
if (sameWeight && i > 0
&& weight != getWeight(invokers.get(i - 1), invocation)) {
sameWeight = false; // Calculate whether all weights are the same
}
}
if (totalWeight > 0 && ! sameWeight) {
// If the weights are not the same and the weight is greater than 0, press total weights random
int offset = random.nextInt(totalWeight);
// and determine which segment the random value falls on
for (int i = 0; i < length; i++) {
offset -= getWeight(invokers.get(i) , invocation);
if (offset < 0) {
return invokers.get(i);
}
}
}
// equally random if the weights are the same or 0
return invokers.get(random.nextInt(length));
}
}
/**
* RoundRobin LoadBalance
* Round robin, set the round robin ratio according to the weight after the convention.
* There is a problem with slow providers accumulating requests. The second machine is slow, but it doesn't hang. When the request is transferred to the second machine, it is stuck there. All requests are stuck on the second machine.
* @author junlee 2016- 08-07
* @Email: [email protected]
* @version
*/
public class RoundRobinLoadBalance extends AbstractLoadBalance {
public static final String NAME = "roundrobin";
private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
private final ConcurrentMap<String, AtomicPositiveInteger> weightSequences = new ConcurrentHashMap<String, AtomicPositiveInteger>();
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
int length = invokers.size(); // 总个数
int maxWeight = 0; // 最大权重
int minWeight = Integer.MAX_VALUE; // 最小权重
for (int i = 0; i < length; i++) {
int weight = getWeight(invokers.get(i),invocation);
maxWeight = Math.max(maxWeight, weight); // 累计最大权重
minWeight = Math.min(minWeight, weight); // 累计最小权重
}
if (maxWeight > 0 && minWeight < maxWeight) { // 权重不一样
AtomicPositiveInteger weightSequence = weightSequences.get(key);
if (weightSequence == null) {
weightSequences.putIfAbsent(key, new AtomicPositiveInteger());
weightSequence = weightSequences.get(key);
}
int currentWeight = weightSequence.getAndIncrement() % maxWeight;
List<Invoker<T>> weightInvokers = new ArrayList<Invoker<T>>();
for (Invoker<T> invoker : invokers) { // 筛选权重大于当前权重基数的Invoker
if (getWeight(invoker, invocation) > currentWeight) {
weightInvokers.add(invoker);
}
}
int weightLength = weightInvokers.size();
if (weightLength == 1) {
return weightInvokers.get(0);
} else if (weightLength > 1) {
invokers = weightInvokers;
length = invokers.size();
}
}
AtomicPositiveInteger sequence = sequences.get(key);
if (sequence == null) {
sequences.putIfAbsent(key, new AtomicPositiveInteger());
sequence = sequences.get(key);
}
// modulo round robin
return invokers.get(sequence.getAndIncrement() % length);
}
}
/**
* LeastActive LoadBalance
* Minimum number of active calls, random for the same active number, active number refers to the difference between the counts before and after the call.
* Make the slow provider receive fewer requests, because the slower the provider's count difference before and after the call will be larger.
* @author junlee 2016-08-07
* @Email: [email protected]
* @version
*/
public class LeastActiveLoadBalance extends AbstractLoadBalance {
public static final String NAME = "leastactive";
private final Random random = new Random();
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size(); // total number
int leastActive = -1; // smallest Active count
int leastCount = 0; // Number of the same minimum active count
int[] leastIndexs = new int[length]; // Subscript of the same minimum active count
int totalWeight = 0; // Total weight
int firstWeight = 0; // The first weight is used to calculate whether it is the same
boolean sameWeight = true; // whether all weights are the same
for (int i = 0; i < length; i++) {
Invoker<T> invoker = invokers.get(i) ;
int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive(); // active count
int weight = invoker.getUrl().getMethodParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT); // weight
if (leastActive == -1 || active < leastActive) { // find less active count, restart
leastActive = active; // record the minimum active count
leastCount = 1; // re-count the same minimum active count
leastIndexs[0] = i; // re-record the minimum active count subscript
totalWeight = weight; / / Re-accumulate the total weight
firstWeight = weight; // Record the first weight
sameWeight = true; // Restore the same weight identifier
} else if (active == leastActive) { // Accumulate the same minimum active count
leastIndexs[leastCount ++] = i; // Accumulate the same minimum active subscript
totalWeight += weight; // Accumulate total weight
// Check if all weights are the same
if (sameWeight && i > 0
&& weight != firstWeight) {
sameWeight = false;
}
}
}
// assert(leastCount > 0)
if (leastCount == 1) {
// if there is only one minimum Then return directly
return invokers.get(leastIndexs[0]);
}
if (! sameWeight && totalWeight > 0) {
// If the weights are not the same and the weight is greater than 0, random according to the total weight
int offsetWeight = random.nextInt(totalWeight) ;
// and determine which segment the random value falls on
for (int i = 0; i < leastCount; i++) {
int leastIndex = leastIndexs[i];
offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
if (offsetWeight <= 0)
return invokers.get(leastIndex);
}
}
// if the weight is the same or the weight is 0 Equal random
return invokers.get(leastIndexs[random.nextInt(leastCount)]);
}
}
/**
* ConsistentHash LoadBalance
* Consistent Hash, requests with the same parameters are always sent to the same provider.
* When a provider hangs, the request originally sent to the provider will be spread to other providers based on virtual nodes, and will not cause drastic changes.
* @author junlee 2016-08-07
* @Email: [email protected]
* @version
*/
public class ConsistentHashLoadBalance extends AbstractLoadBalance {
private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();
@SuppressWarnings("unchecked")
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
int identityHashCode = System.identityHashCode(invokers);
ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
if (selector == null || selector.getIdentityHashCode() != identityHashCode) {
selectors.put(key, new ConsistentHashSelector<T>(invokers, invocation.getMethodName(), identityHashCode));
selector = (ConsistentHashSelector<T>) selectors.get(key);
}
return selector.select(invocation);
}
private static final class ConsistentHashSelector<T> {
private final TreeMap<Long, Invoker<T>> virtualInvokers;
private final int replicaNumber;
private final int identityHashCode;
private final int[] argumentIndex;
public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
this.identityHashCode = System.identityHashCode(invokers);
URL url = invokers.get(0).getUrl();
this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
String[] index = Constants.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]);
}
for (Invoker<T> invoker : invokers) {
for (int i = 0; i <replicaNumber / 4; i++) {
byte[] digest = md5(invoker.getUrl().toFullString() + i);
for (int h = 0; h < 4; h++) {
long m = hash(digest, h);
virtualInvokers.put(m, invoker);
}
}
}
}
public int getIdentityHashCode() {
return identityHashCode;
}
public Invoker<T> select(Invocation invocation) {
String key = toKey(invocation.getArguments());
byte[] digest = md5(key);
Invoker<T> invoker = sekectForKey(hash(digest, 0));
return invoker;
}
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> sekectForKey(long hash) {
Invoker<T> invoker;
Long key = hash;
if (!virtualInvokers.containsKey(key)) {
SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key);
if (tailMap.isEmpty()) {
key = virtualInvokers.firstKey();
} else {
key = tailMap.firstKey();
}
}
invoker = virtualInvokers.get(key);
return invoker;
}
private long hash(byte[] digest, int number) {
return (((long) (digest[3 + number * 4] & 0xFF) << 24)
| ((long) (digest[2 + number * 4] & 0xFF) << 16)
| ((long) (digest[1 + number * 4] & 0xFF) <<
| (digest[0 + number * 4] & 0xFF))
& 0xFFFFFFFFL;
}
private byte[] md5(String value) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e.getMessage(), e);
}
md5.reset();
byte[] bytes = null;
try {
bytes = value.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e.getMessage(), e);
}
md5.update(bytes);
return md5.digest();
}
}
}
Guess you like
Origin http://10.200.1.11:23101/article/api/json?id=326696083&siteId=291194637
Recommended
Ranking