随机数权重算法
假设一个服务service1 分布在一个含有4个节点(A, B, C, D)的集群上。 权重分别为1,2,3,4。那么一个请求service1 的 到 A,B,C,D 节点的概率为 10%,20%,30%,40%。
一个对于service1 的请求 会被随机分配一个数字。这个数字是A,B,C,D 节点权重之和范围类随机出来的。我们的例子中权重之和是10,所以随机数范围是【0,9】
假设随机出了数字 6, 那么选择的节点是D。原因是:6-1=5>0, 5-2=3>0, 3-3=0>=0, 0-4<0。原理是依次和各个权重相减,直到<0为止,找到D节点。
/**
* 随机数权重算法
*/
public Node randomWeightAlgorithm() {
List<Node> nodeList = new ArrayList<>();
nodeList.add(new Node(1, 1));
nodeList.add(new Node(2, 2));
nodeList.add(new Node(3, 3));
nodeList.add(new Node(4, 4));
int len = nodeList.size();
int totalWeight = 0;
boolean sameWeight = true;
for (int i = 0; i < len; i++) {
int weight = nodeList.get(0).weight;
totalWeight+=weight;
if (sameWeight && i > 0 && weight != nodeList.get(i-1).weight) {
// 节点权重都不相同
sameWeight = false;
}
}
if (totalWeight > 0 && !sameWeight) {
int offset = ThreadLocalRandom.current().nextInt(totalWeight);
for (Node aNodeList : nodeList) {
offset -= aNodeList.weight;
if (offset < 0) {
return aNodeList;
}
}
}
// 如果节点权重相同, 则随机返回一个节点
return nodeList.get(ThreadLocalRandom.current().nextInt(len));
}
基于权重的轮询算法
普通的轮询就是权重相同的情况。如果服务service1 部署在集群A,B,C,D四台机器上。那么第一个服务请求被路由到机器A,第二个请求被路由到机器B, 依次类推。
当4个节点权限不同时,维护一个map<服务标记位,权重计数器>。 每次请求过来,权重计数器+1。
每次请求过来,找出出最大,最小的权重。(权重计数器+1) % 最大权重, 得到这次请求的权重基准线。找出>权重基准线的节点们,再用当前权限基准线 % 这些节点的数量(Size) = 要选择的节点的下标。就可以获得选择的节点。
// <唯一标识接口的id, 权重计数器>
private final ConcurrentMap<Integer, AtomicPositiveInteger> sequences = new ConcurrentHashMap<>();
/**
* 基于权重的轮训调度算法
*/
public Node roundRobinLoadBalance() {
// mock 4 个 节点
List<Node> nodeList = new ArrayList<>();
nodeList.add(new Node(1, 1));
nodeList.add(new Node(2, 2));
nodeList.add(new Node(3, 3));
nodeList.add(new Node(4, 4));
// 假设一个id = 5 标识一个唯一的接口
int key = 5;
int length = nodeList.size();
int maxWeight = 0;
int minWeight = Integer.MAX_VALUE;
// 找出最大,最小权重值
for (int i = 0; i< length; i++) {
int weight = nodeList.get(i).weight;
maxWeight = Math.max(maxWeight, weight);
minWeight = Math.min(minWeight, weight);
}
// 4个节点的权重不同
if (maxWeight > 0 && minWeight < maxWeight) {
AtomicPositiveInteger weightSequence = sequences.get(key);
if (weightSequence == null) {
sequences.putIfAbsent(key, new AtomicPositiveInteger());
}
int currentWeight = weightSequence.getAndIncrement() % maxWeight;
List<Node> nodesAfterSelect = new CopyOnWriteArrayList<>();
// 筛选权重>当前权重的节点们
for (Node node : nodeList) {
if (node.weight > currentWeight) {
nodesAfterSelect.add(node);
}
}
int weightLength = nodesAfterSelect.size();
if (weightLength == 1) {
return nodesAfterSelect.get(0);
}
if (weightLength > 1) {
return nodesAfterSelect.get(currentWeight % weightLength);
}
}
// 四个节点权重相同
AtomicPositiveInteger sequence = sequences.get(key);
if (sequence == null) {
sequences.putIfAbsent(key, new AtomicPositiveInteger());
sequence = sequences.get(key);
}
// 通过取模来轮训某个节点
return nodeList.get(sequence.getAndIncrement() % length);
}