# 加权轮询算法(wrr)，这个考点，概率有点高！

4,066

``````upstream backend {
ip_hash;
server 192.168.1.232 weight=4;
server 192.168.1.233 weight=3;
server 192.168.1.234 weight=1;
}
``````

## 1. 核心数据结构

``````public class Element {
protected String peer;
protected int weight;

public Element(String peer, int weight){
this.peer = peer;
this.weight = weight;
}
}
``````

``````public interface IWrr {
String next();
}
``````

``````Element[] elements = new Element[]{
new Element("A", 7),
new Element("B", 2),
new Element("C", 1),
};
int count = 10;
IWrr wrr = new WrrSecurityLoopTreeMap(elements);
for (int i = 0; i < count; i++) {
System.out.print(wrr.next() + ",");
}
System.out.println();
``````

## 2. 随机数版本

next方法的时间复杂度，在最坏的情况下是O(n)。

``````public class WrrRnd implements IWrr {
final int total;
final Element[] elements;
final Random random = new SecureRandom();

public WrrRnd(Element[] elements) {
this.total = Arrays.stream(elements)
.mapToInt(ele -> ele.weight)
.sum();

this.elements = elements;
}

@Override
public String next() {
final int n = elements.length;
int index = n - 1;
int hit = random.nextInt(total);

for(int i = 0; i < n; i++){
if(hit >= 0) {
hit -= elements[i].weight;
}else{
index = i - 1;
break;
}
}

return elements[index].peer;
}
}
``````

## 3. 递增版本

``````//原来的
int hit = random.nextInt(total);
``````

``````int hit = count.getAndIncrement() % total;
``````

## 4. 红黑树版本

``````public class WrrSecurityLoopTreeMap implements IWrr {
final int total;
final AtomicInteger count = new AtomicInteger();
final TreeMap<Integer, Element> pool = new TreeMap<>();

public WrrSecurityLoopTreeMap(Element[] elements) {
int total = 0;
for (Element ele : elements) {
total += ele.weight;
pool.put(total - 1, ele);
}
this.total = total;
}

@Override
public String next() {
final int modulo = total;
for (; ; ) {
int hit = count.get();
int next = (hit + 1) % modulo;
if (count.compareAndSet(hit, next) && hit < modulo) {
return pool.ceilingEntry(hit).getValue().peer;
}
}
}
}
``````

## 5. LVS版本

``````http://kb.linuxvirtualserver.org/wiki/Weighted_Round-Robin_Scheduling
``````

``````public class WrrGcd implements IWrr {
final int gcd;
final int max;
final Element[] elements;

public WrrGcd(Element[] elements) {
Integer gcd = null;
int max = 0;
for (Element ele : elements) {
gcd = gcd == null ? ele.weight : gcd(gcd, ele.weight);
max = Math.max(max, ele.weight);
}
this.gcd = gcd;
this.max = max;
this.elements = elements;
}

int i = -1;
int cw = 0;
@Override
public String next() {
for (; ; ) {
final int n = elements.length;
i = (i + 1) % n;
if (i == 0) {
cw = cw - gcd;
if (cw <= 0) {
cw = max;
if (cw == 0) {
return null;
}
}
}
if(elements[i].weight >= cw){
return elements[i].peer;
}
}
}

private int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
}
``````

## 6. Nginx版本

nginx这个版本就更上一层楼，可以达到`A,A,B,A,A,C,A,A,B,A,`的效果。在保证准确的权重前提下，实现了调用尽量的分散。

``````https://tenfy.cn/2018/11/12/smooth-weighted-round-robin/
``````

``````public class WrrSmooth implements IWrr {
class Wrr {
Element ele;
int current = 0;
Wrr(Element ele){
this.ele = ele;
}
}

final Wrr[] cachedWeights;

public WrrSmooth(Element[] elements) {
this.cachedWeights = Arrays.stream(elements)
.map(Wrr::new)
.collect(Collectors.toList())
.toArray(new Wrr[0]);
}

@Override
public String next() {
int total = 0;
Wrr shed = cachedWeights[0];

for(Wrr item : cachedWeights){
int weight = item.ele.weight;
total +=  weight;

item.current += weight;
if(item.current > shed.current){
shed = item;
}
}
shed.current -= total;
return shed.ele.peer;
}
}
``````

Nginx的这个版本，写法非常简单。建议好好理解，掌握红黑树和Ningx版本的写法即可。