Load Balance算法
列举几种开源框架的常用实现
Dubbo
-
Random
- random.nextInt(length),
-
带权重Random
- 带权重random.next(totalWeight)=offset 再获取分配invoke.getweight;offset-weight <0 当前调用者
-
RoundRobin
- counter++%length 余数,带权重同上
-
least active
- 最少活跃数,每次调用前后计数 (time),慢的节点数值大
-
ConsistentHashLoadBalance
- 略。常规的一致性hash实现方式
netflix ribbon
-
BestAvailableRule 最小并发数 ActiveRequestsCount 最少的被选中
-
AvailabilityFilteringRule 过滤熔断的连接和Active connections(ActiveRequestsCount)超过某个阀值的 -- 简单说,过滤掉不健康的和繁忙的节点
-
WeightedResponseTimeRule 响应时间小的根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。类似 Dubbo - least active
-
RoundRobinRule
- 同Dubbo RR类似
-
RandomRule
- 同Dubbo Random类似
-
ZoneAvoidanceRule 网络分区
- 如果这个 ip 区域内有一个或多个实例不可达或响应变慢,都会降低该 ip 区域内其他 ip 被选中的权重。
-
retryRule
- 其实就是RoundRobin策略的增强版,RoundRobin策略服务不可用时不做处理,重试策略服务不可用时会重新尝试集群中的其他节点
nginx
- Round Robin
- 不带weight 类似dubbo RR
- 支持权重的Round Robin
-
经典算法 s[i].cw1000/s[i+1].cw>s[i].w1000/s[i+1].w cw=cw-1
-
smooth算法 best=max-weight-idx
- s[i].cw+=s[i].effective s[best].cw-total
- 平滑 effective_weight 每次错误-1 选中再+1
- Hash 根据ip/url/url+params计算hash value
- Less conn 每个work less conn,如何返回多个,再round robin
LVS
-
round robin(weight)
- gcd公约数,cw=cw-gcd 比较s.weight >cw 选中
-
Round robin counter++%len
-
Less conn
Talk is cheap,show me the code
LVS RR,基于最大公约数实现
public class LvsRoundRobinWeight implements LoadBalance{
private static List<Node> servers = new ArrayList<Node>();
public int idx=-1;
public int cw = 0;
public static int n=0;
public static int num[]=new int[3];
public static int gcd = 0;
static {
servers.add(new Node(5,"a",5));
servers.add(new Node(2,"b",2));
servers.add(new Node(3,"c",3));
n = servers.size();
for (int i=0;i<n;i++) {
num[i] = servers.get(i).getWeight();
}
gcd = getGCD(num, n);
}
@Override
public int getPeerIndex() {
while (true){
idx = (idx + 1) % n;
if(idx==0){
cw = cw - gcd;
if(cw<=0){
cw = getMax();
if(cw==0){
return -1;
}
}
}
if(servers.get(idx).getCurrent_weight()>=cw){
return idx;
}
}
}
private static int getMax(){
int max = 0;
for (int i = 0; i < servers.size(); i++) {
int temp=servers.get(i).getCurrent_weight();
if(temp>max){
max=temp;
}
}
return max;
}
private static int getGCD(int[] num,int n){
if (n == 1)
return num[n - 1];
return gcd(num[n-1],getGCD(num,n-1));
}
private static int gcd(int a,int b){
if (b == 0){
return a;
}else{
return gcd(b, a%b);
}
}
public static void main(String[] args) {
LvsRoundRobinWeight lvsRoundRobinWeight = new LvsRoundRobinWeight();
for (int i = 0; i < 10; i++) {
int result = lvsRoundRobinWeight.getPeerIndex();
lvsRoundRobinWeight.idx = result;
System.out.println(servers.get(result));
}
}
}
Nginx RR 实现,支持权重
/**
* https://github.com/yaoweibin/nginx_tcp_proxy_module/blob/master/ngx_tcp_upstream_round_robin.c
*/
public class NginxRoundRobinWeight implements LoadBalance {
private static List<Node> servers = new ArrayList<Node>();
static {
servers.add(new Node(5,"a",5));
servers.add(new Node(2,"b",2));
servers.add(new Node(1,"c",1));
}
@Override
public int getPeerIndex() {
int n = 0;
for(;;) {//must return
for (int i = 0; i < servers.size(); i++) {
Node node = servers.get(i);
if (node.getCurrent_weight() <= 0) {
continue;
}
n = i;
while (i < servers.size()-1) {
i++;
if (servers.get(i).getCurrent_weight() <= 0) {
continue;
}
if (servers.get(n).getCurrent_weight() * 1000 / servers.get(i).getCurrent_weight()
> servers.get(n).getWeight() * 1000 / servers.get(i).getWeight()) {
return n;
}
n = i;
}
return n;
}
//recover weight
for (int i = 0; i < servers.size(); i++) {
servers.get(i).setCurrent_weight(servers.get(i).getWeight());
}
}
}
public static void main(String[] args) {
NginxRoundRobinWeight weight = new NginxRoundRobinWeight();
for (int i = 0; i < 8; i++) {
int idx = weight.getPeerIndex();
Node node1 = servers.get(idx);
node1.setCurrent_weight(node1.getCurrent_weight() - 1);
System.out.println(node1.toString());
}
}
}
Nginx RR smooth实现
public class NginxRoundRobinSmooth implements LoadBalance {
private static List<Node> servers = new ArrayList<Node>();
static {
servers.add(new Node(5,"a"));
servers.add(new Node(1,"b"));
servers.add(new Node(1,"c"));
}
@Override
public int getPeerIndex() {
int best = getMax();
int total = 0;
for (int i = 0; i < servers.size(); i++) {
Node server = servers.get(i);
server.setCurrent_weight(server.getCurrent_weight() + server.getEffective_weight());
total = getTotal();
if (server.getCurrent_weight() > servers.get(best).getCurrent_weight()) {
best = i;
}
}
servers.get(best).setCurrent_weight(servers.get(best).getCurrent_weight() - total);
return best;
}
private static int getTotal(){
int total = 0;
for (int i = 0; i < servers.size(); i++) {
total += servers.get(i).getEffective_weight();
}
return total;
}
private static int getMax(){
int max = 0;
int idx = 0;
for (int i = 0; i < servers.size(); i++) {
int temp=servers.get(i).getEffective_weight();
if(temp>max){
max=temp;
idx = i;
}
}
return idx;
}
public static void main(String[] args) {
NginxRoundRobinSmooth smooth = new NginxRoundRobinSmooth();
for (int j = 0; j < 14; j++) {
int best = smooth.getPeerIndex();
System.out.println(servers.get(best));
}
}
}