平滑的基于权重的轮询算法

i := 0; i < 10; i++ { fmt.Printf("%s ", w.Next()) } // Output: a c b a a c a b c a}

LVS算法

LVS使用的另外一种算法,它的算法的介绍可以参考它的网站的wiki。

算法用伪代码表示如下:

1234567891011121314151617181920Supposing that there is a server set S = {S0, S1, …, Sn-1};W(Si) indicates the weight of Si;i indicates the server selected last time, and i is initialized with -1;cw is the current weight in scheduling, and cw is initialized with zero; max(S) is the maximum weight of all the servers in S;gcd(S) is the greatest common divisor of all server weights in S;while (true) { i = (i + 1) mod n; if (i == 0) { cw = cw - gcd(S); if (cw <= 0) { cw = max(S); if (cw == 0) return NULL; } } if (W(Si) >= cw) return Si;}

可以看到它的代码逻辑比较简单,所以性能也很快,但是如果服务器的权重差别较多,就不会像Nginx那样比较平滑,可以在短时间内对权重很大的那台服务器压力过大。

使用weighted库和上面一样简单,只是把类型W换成W2即可:

123456789101112func ExampleW_() { w := &W2{} w.Add("a", 5) w.Add("b", 2) w.Add("c", 3) for i := 0; i < 10; i++ { fmt.Printf("%s ", w.Next()) } // Output: a a a c a b c a b c}

性能比较

可以看到,上面两种方法的使用都非常的简单,只需生成一个相应的W对象,然后加入服务器和对应的权重即可,通过Next方法就可以获得下一个服务器。

如果服务器的权重差别很大,出于平滑的考虑,避免短时间内会对服务器造成冲击,你可以选择Nginx的算法,如果服务器的差别不是很大,可以考虑使用LVS的算法,因为测试可以看到它的性能要好于Nginx的算法:

12BenchmarkW1_Next-4 20000000 50.1 ns/op 0 B/op 0 allocs/opBenchmarkW2_Next-4 50000000 29.1 ns/op 0 B/op 0 allocs/op

实际上两者的性能都非常的快,十个服务器的每次调度也就是几十纳秒的级别,而且没有额外的对象分配,所以无论使用哪种算法,这个调度不应成为你整个系统的瓶颈。