dubbo中负载均衡算法
当dubbo中服务的提供者存在多个时,就存在服务的集群,集群中如何分配服务的调用就存在一些算法,选择合适的服务来提供服务。
轮询负载均衡算法RoundRobinLoadBalance
轮询顾名思义就是按照顺序一个一个来提供服务,假设有三个服务1,2,3,首先执行服务1,然后2,然后3,紧接着服务1
- 直接上代码
// 定义一个类似计数器
private static AtomicInteger atomicIndexDirect = new AtomicInteger(0);
private List<String> appServerAddress = Arrays.asList("http://localhost:1000/1","http://localhost:1001/1","http://localhost:1001/1")
// 服务提供者集群中轮询调用
if (appServerAddressList.size() == 1) {
appServerAddress = appServerAddressList.get(0);
} else {
// 计数器加一
int index = atomicIndexDirect.incrementAndGet();
// 判断是否越界,越界回到原点
if (index > appServerAddressList.size() - 1) {
index = 0;
atomicIndexDirect.set(index);
}
appServerAddress = appServerAddressList.get(index);
}
权重随机算法的 RandomLoadBalance
假设有三台服务器[A,B,C],我们设置权重分别是5,3,2,权重总和为10,对于这三台服务器,可以认为A服务器在[0,5),B服务器在[5,8),C服务器在[8,10],此时提供一个随机数生成器(此处认为该随机数生成器算法足够好,产生的数据足够随机),那么让其生成一个[0,10)的随机数,这个数据将会落在其中一个区段,此时就将请求分发到对应
思路就是给每个服务分配权重,然后获取总权重,取总权重生成一个小于总权重的随机数,然后将随机数以每个服务的权重数比对是否在改服务区间
- 上代码dubbo源码
package org.apache.dubbo.rpc.cluster.loadbalance;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* random load balance.
*/
public class RandomLoadBalance extends AbstractLoadBalance {
public static final String NAME = "random";
@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
// Dubbo多个节点都会包装成invoker模型的形式
int length = invokers.size();
boolean sameWeight = true;
// 每一个invoker的权重
int[] weights = new int[length];
// 尝试获取第一个invoker的权重
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;
}
}
if (totalWeight > 0 && !sameWeight) {
// 基于总权重,随机一个数据出来
int offset = ThreadLocalRandom.current().nextInt(totalWeight);
// 求随机出来的数据落在哪个区间段
for (int i = 0; i < length; i++) {
offset -= weights[i];
if (offset < 0) {
return invokers.get(i);
}
}
}
//如果权重相等,则随机一个数出来
return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
}
权重轮询负载均衡算法WeightRoundRobinLoadBalance
思路:将所有的请求个数都保存到map集合中,key是调用的接口,方法,参数类型,value是调用的次数,根据每个服务的权重获取最大权重和最小权重,和总的权重,以及将每个服务的的请求地址和权重保存到map集合中,通过遍历总的权重,利用接口调用次数对总权重取余(将请求分布在权重中),再遍历保存的地址和权重,如何满足条件则返回,不满足则将调用次数减少
当所有服务的权重一致时候,则变为轮询调用
- 直接上代码
package com.yu.dubbo.core.cluster.loadbalance;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
public class WeightRoundRobinLoadBalance extends AbstractLoadBalance {
public static final String NAME = "weightroundrobin";
protected static class WeightedRoundRobin {
private AtomicLong current = new AtomicLong(0);
private long getAndIncrement() {
return current.getAndIncrement();
}
}
private ConcurrentMap<String, WeightedRoundRobin> methodWeightMap = new ConcurrentHashMap<>();
@Override
protected String doSelect(List<String> providers, String interfaceName, String methodName) {
String key = interfaceName + "." + methodName;
int length = providers.size();
int maxWeight = 0;
int minWeight = Integer.MAX_VALUE;
final LinkedHashMap<String, IntegerWrapper> invokerToWeightMap = new LinkedHashMap<>();
int weightSum = 0;
//初始化maxWeight,minWeight,weightSum,invokerToWeightMap
for (int i = 0; i < length; i++) {
int weight = getWeight(providers.get(i));
maxWeight = Math.max(maxWeight, weight); // Choose the maximum weight
minWeight = Math.min(minWeight, weight); // Choose the minimum weight
if (weight > 0) {
invokerToWeightMap.put(providers.get(i), new IntegerWrapper(weight));
weightSum += weight;
}
}
// 获取自增调用次数
WeightedRoundRobin weightedRoundRobin = methodWeightMap.get(key);
if (weightedRoundRobin == null) {
methodWeightMap.putIfAbsent(key, new WeightedRoundRobin());
weightedRoundRobin = methodWeightMap.get(key);
}
// 当前接口调用总次数
long currentSequence = weightedRoundRobin.getAndIncrement();
//当权重不一样的时候,通过加权轮询获取到invoker,权值越大,则被选中的几率也越大
if (maxWeight > 0 && minWeight < maxWeight) {
long mod = currentSequence % weightSum;
for (int i = 0; i < maxWeight; i++) {
//遍历invoker的数量
for (Map.Entry<String, IntegerWrapper> each : invokerToWeightMap.entrySet()) {
final String k = each.getKey();
//invoker的权重
final IntegerWrapper v = each.getValue();
if (mod == 0 && v.getValue() > 0) {
return k;
}
if (v.getValue() > 0) {
//当前invoker的可调用次数减1
v.decrement();
mod--;
}
}
}
}
// Round robin 权重一样的情况下,就取余的方式获取到invoker
return providers.get((int) (currentSequence % length));
}
private static final class IntegerWrapper {
private int value;
public IntegerWrapper(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public void decrement() {
this.value--;
}
}
}
一致性哈希算法
使用hash算法的同时,新增服务节点需要对所有的服务节点重新映射关系,影响比较大,所有就由了一致性hash算法,增加服务节点时只会小范围影响来重新建立映射关系,还可以通过添加虚拟节点,使得请求尽量分散一些
最小活跃数算法
最小活跃数就是取响应时间最快的服务,哪个响应请求快,就由哪个服务器处理