经典版算法
static ngx_uint_t ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers)
{
ngx_uint_t i, n, reset = 0;
ngx_http_upstream_rr_peer_t *peer;
peer = &peers->peer[0]; //peer指向后端服务器列表
for ( ;; ) {
for (i = 0; i < peers->number; i++) {
if (peer[i].current_weight <= 0) {
continue;
}
//n为第一个current_weight大于0的服务器下标
n = i;
while (i < peers->number - 1) {
i++; //i为n的下一个服务器开始遍历
if (peer[i].current_weight <= 0) {
continue;
}
//是否选择该服务器的关键
//核心思想:当前权重比例(会变小)是否大于初始化的比例
//如果大于了,则选择当前的,因为后面的服务器已经刚被选用过了
//这也导致了选中服务器是从尾部开始的
if (peer[n].current_weight * 1000 / peer[i].current_weight
> peer[n].weight * 1000 / peer[i].weight)
{
return n;
}
n = i;
}
if (peer[i].current_weight > 0) {
n = i;
}
return n;
}
//初始为0,所以第二次循环到此条件才成立,注意是后置自增。
if (reset++) {
return 0;
}
for (i = 0; i < peers->number; i++) {
peer[i].current_weight = peer[i].weight;
}
}
}
代码说明:
(1) peer[n].weight:后端服务器初始权重。
(2) peer[n].current_weight:后端服务器当前权重,初始情况等于peer[n].weight。
(3) peers->number:后端服务器的个数
(4) peers->peer[0]:一个数组的第一个元素,这个数组的每个元素对应一个后端服务器。
(5) 一旦某个后端服务器n被选中后,会在其他处理函数中执行peer[n].current_weight - 1。
(6) 代码18行乘以1000是为了避免浮点处理,所以直接报被除数放大1000倍,也就是间接把精度提升到小数点后三位,注意这里是权值的比较,因此把两边权值都放大1000倍并不会影响最终的比较结果。
举个例子来说明这个算法:{ a, b, c }三个服务器,weight值是{ 5, 1, 2 },那么分配的过程参见下面这张表:
第一次循环因为比例没变化,会循环到最后一个服务器并选中使用,此后就有了变化:

- 这么看效果还不错,但是如果仔细看会发现有缺陷。就是weight小的server分配不均。其实b在第四或者第五位被分配是比较好的。
开发版算法
For edge case weights like { 5, 1, 1 } we now produce { a, a, b, a, c, a, a }
sequence instead of { c, b, a, a, a, a, a } produced previously.
Algorithm is as follows: on each peer selection we increase current_weight
of each eligible peer by its weight, select peer with greatest current_weight
and reduce its current_weight by total number of weight points distributed
among peers.
In case of { 5, 1, 1 } weights this gives the following sequence of
current_weight's:
a b c
0 0 0 (initial state)
5 1 1 (a selected)
-2 1 1
3 2 2 (a selected)
-4 2 2
1 3 3 (b selected)
1 -4 3
6 -3 4 (a selected)
-1 -3 4
4 -2 5 (c selected)
4 -2 -2
9 -1 -1 (a selected)
2 -1 -1
7 0 0 (a selected)
0 0 0
To preserve weight reduction in case of failures the effective_weight
variable was introduced, which usually matches peer's weight, but is
reduced temporarily on peer failures.
毒化的加权动态优先级算法,最大的特点有两点:
- 一是优先级current_weight的变化量是权effective_weight,
- 二是对所选server的优先级进行大规模毒化,毒化程度是所有server的权值之和。- 这种算法的结果特点一定是权高的server一定先被选中,并且更频繁的被选中,而权低的server也会慢慢的提升优先级而被选中。
核心思想:
1 选当前所有节点中w最大的
2 最大的则削减,削减的量为其余的总量(total-本身);其他的节点则增加一倍的量