Nginx 加权轮训实现原理

经典版算法

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-本身);其他的节点则增加一倍的量

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容