Nginx源码学习-负载均衡-关于轮询策略原理的自我理解

由于曾经缺乏手撕源码的经验,这段时间从零开始一点点啃nginx源码实在是啃的过于痛苦,加之自己的代码水平实在有限,只能结合多方大佬的源码分析文章以及自己的“意会”,用自己能理解的大白话梳理一下我所理解的算法实现过程。

轮询算法的原理就不作叙述了,这里直接贴出每轮轮询中选出最后结果的方法ngx_http_upstream_get_peer的源码:

static ngx_http_upstream_rr_peer_t *
ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
{
    time_t                        now;
    uintptr_t                     m;
    ngx_int_t                     total;
    ngx_uint_t                    i, n, p;
    ngx_http_upstream_rr_peer_t  *peer, *best;

    now = ngx_time();

    best = NULL;
    total = 0;

#if (NGX_SUPPRESS_WARN)
    p = 0;
#endif

    for (peer = rrp->peers->peer, i = 0; //peer表示当前服务器
         peer;
         peer = peer->next, i++)
    {
		//计算当前服务器的标记位在位图中的位置
        n = i / (8 * sizeof(uintptr_t));
        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));

		//已经选择过,跳过
        if (rrp->tried[n] & m) {
            continue;
        }

		//当前服务器对象以宕机
        if (peer->down) {
            continue;
        }

		//一段时间内失败次数超过最大失败次数
        if (peer->max_fails //设定了最大失败次数
            && peer->fails >= peer->max_fails
            && now - peer->checked <= peer->fail_timeout)
        {
            continue;
        }

        if (peer->max_conns && peer->conns >= peer->max_conns) {
            continue;
        }

		/*遍历服务列表的过程中,每遍历到一个服务,会在该服务的current_weight上加上其对应的effective_weight*/
        peer->current_weight += peer->effective_weight; 
		/*total变量记录了针对一个服务列表的一次轮询过程中轮询到的所有服务的effective_weight总和*/
        total += peer->effective_weight;

		//服务正常,effective_weight逐渐恢复正常
        if (peer->effective_weight < peer->weight) {
            peer->effective_weight++;
        }

        if (best == NULL || peer->current_weight > best->current_weight) {
            best = peer;
            p = i;
        }
    }

    if (best == NULL) {
        return NULL;
    }

	//设置“当前”结点为最佳结点
    rrp->current = best;

    n = p / (8 * sizeof(uintptr_t));
    m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));

	//修改位图,表示该结点已被使用
    rrp->tried[n] |= m;

	//取到该后端服务后,都会把该对象peer的current_weight减去total。因为该服务器刚被选中过,因此要降低权值
    best->current_weight -= total;

    if (now - best->checked > best->fail_timeout) {
        best->checked = now; //设置最佳结点的检查时间为now;
    }

    return best;
}

其中我觉得最能表现该算法核心“轮询”二字的关键代码部分:

for (peer = rrp->peers->peer, i = 0; peer;peer = peer->next, i++)
{
	... 
	peer->current_weight += peer->effective_weight; 
	total += peer->effective_weight;
	...
	if (best == NULL || peer->current_weight > best->current_weight) {
            best = peer;
            p = i;
    }
    ...
}

其中,peer是指当前遍历到的服务器结点,current_weight指该结点的当前权值,total指本轮遍历中所有结点的总权值,它会累加;也就是说,current_weight只记录当前一次循环的这个peer的权值,而total会记录累加整个循环中所有peer的权值之和。best指最佳结点,循环结束时,即可找到最佳结点best。

找到最佳结点best后应当降低其权值,避免下次轮询的结果还是它,源码是这么做的:

best->current_weight -= total;

我觉得这里是最难理解的,为什么要让当前结点的权值减去总权值呢,这岂不是负数了吗?从这篇博客看出→参考的轮询策略实例,结果还真是负数。结合大佬的文章,我是这样理解这个过程的:

每个结点就像一位勇者,他的hp为current_weight。每次轮询就像一次回合制战斗,会选出当前hq最高的那位勇者前去攻击。被选择的这位勇者成功击中怪物一次(被选择成为best最佳结点),自己就会被怪物反击,需从hq中扣除一定的血量(即total)。此时这位勇者的hq太低,下次就不会被选取作战了。但每位勇者每回合都会回复一定hq(即effective_weight),也就是说effective_weight越高,其恢复能力越强,因而其没过多久hq又会变的最高,最后总的看下来,的确权值越高,该结点被选择的几率越大!

最后列出几篇十分有帮助的博客:
参考文章1
参考文章2
参考文章3

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
nginx是一款高性能的Web服务器和反向代理服务器。作为一种反向代理服务器,nginx非常擅长处理并发请求和负载均衡。在实际应用中,为了提高网站的性能和可用性,通常会采用nginx负载均衡来分摊请求压力,提高响应速度和稳定性。 nginx常见的几种负载均衡策略包括: 1. 轮询(Round Robin):nginx默认采用轮询策略,将请求按顺序分配给后端服务器,每台服务器处理相同数量的请求。轮询算法简单,负载均衡效果较好,但可能会因为服务器性能和带宽等因素的不同,导致某台服务器负载过高或者过低。 2. IP哈希(IP Hash):IP哈希策略是根据客户端的IP地址进行哈希运算,将结果映射到后端服务器,保证相同IP的用户会访问同一台服务器。这种策略能够提高缓存效果和用户体验,但可能会产生哈希碰撞的问题,导致负载不均衡。 3. 最少连接(Least Connections):最少连接策略是将请求发送到当前连接数最少的服务器。这种策略适用于长连接和持久连接的应用场景,能够避免服务器因为长连接而导致连接数过多,但可能会因为配置问题或者异常情况导致某台服务器负载过高。 4. URL哈希(Hash):URL哈希策略是根据请求URL进行哈希运算,将结果映射到后端服务器。这种策略适用于有相同URL并发请求的场景,能够提高缓存效果和负载均衡效果。 总之,nginx提供了多种负载均衡策略,可以根据实际应用场景和业务需求来选择合适的策略,从而实现高可用性、高性能的应用服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亚兰德尔

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值