lvs 轮叫调度(Round-Robin)算法 源码分析

1. Round-Robin 算法

轮叫调度(Round Robin Scheduling)算法就是以轮叫的方式依次将请求调度到不同的服务器,即每次调度执行
     i = (i + 1) mod n,
然后选出第i台服务器。

算法的优点是其简洁性,它无需记录当前所有连接的状态,所以它是一种无状态调度。

在linux源码实现时,引入了一个额外条件,当服务器的权值为零时,表示该服务器不可用而不被调度,然后选择下一个服务器。这样做的目的是将服务器切出服务(如屏蔽服务器故障和系统维护),同时与其他加权算法保持一致。所以,算法要作相应的改动,它的算法流程如下:

轮叫调度算法流程
假设有一组服务器S = {S0, S1, …, Sn-1},变量i表示上一次选择的
服务器,W(Si)表示服务器Si的权值。变量i被初始化为n-1,其中n > 0。

j = i;
do {
    j = (j + 1) mod n;
    if (W(Sj) > 0) {
        i = j;
        return Si;
    }
} while (j != i);
return NULL;

轮叫调度算法假设所有服务器处理性能均相同,不管服务器的当前连接数和响应速度。该算法相对简单,不适用于服务器组中处理性能不一的情况,而且当请求服务时间变化比较大时,轮叫调度算法容易导致服务器间的负载不平衡。

虽然Round-Robin DNS方法也是以轮叫调度的方式将一个域名解析到多个IP地址,但轮叫DNS方法的调度粒度是基于每个域名服务器的,域名服务器对域名解析的缓存会妨碍轮叫解析域名生效,这会导致服务器间负载的严重不平衡。这里,IPVS轮叫调度算法的粒度是基于每个连接的,同一用户的不同连接都会被调度到不同的服务器上,所以这种细粒度的轮叫调度要比DNS的轮叫调度优越很多。

2.linux源码分析

/*
 * Round-Robin Scheduling
 */
static struct ip_vs_dest *
ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb,
          struct ip_vs_iphdr *iph)
{
    struct list_head *p;
    struct ip_vs_dest *dest, *last;
    int pass = 0;

    IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);

    spin_lock_bh(&svc->sched_lock);
    p = (struct list_head *) svc->sched_data;
    last = dest = list_entry(p, struct ip_vs_dest, n_list);

    do {
        list_for_each_entry_continue_rcu(dest,
                         &svc->destinations,
                         n_list) {
            if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) &&
                atomic_read(&dest->weight) > 0)
                /* HIT */
                goto out;
            if (dest == last)
                goto stop;
        }
        pass++;
        /* Previous dest could be unlinked, do not loop forever.
         * If we stay at head there is no need for 2nd pass.
         */
    } while (pass < 2 && p != &svc->destinations);

stop:
    spin_unlock_bh(&svc->sched_lock);
    ip_vs_scheduler_err(svc, "no destination available");
    return NULL;

  out:
    svc->sched_data = &dest->n_list;
    spin_unlock_bh(&svc->sched_lock);
    IP_VS_DBG_BUF(6, "RR: server %s:%u "
              "activeconns %d refcnt %d weight %d\n",
              IP_VS_DBG_ADDR(dest->af, &dest->addr), ntohs(dest->port),
              atomic_read(&dest->activeconns),
              atomic_read(&dest->refcnt), atomic_read(&dest->weight));

    return dest;
}

代码分析:
14行:对svc->sched_lock上锁;
15行:获取list head指针;
16行:获取list head的n_list数据成员;
18-33行: Round-Robin 算法
    严格的说19-28才是真正的Round-Robin算法。
    list_for_each_entry_continue_rcu其实是一个宏,定义位于include/linux/rculist.h文件,具体如下:

/**
 * list_for_each_entry_continue_rcu - continue iteration over list of given ty    pe
 * @pos:        the type * to use as a loop cursor.
 * @head:       the head for your list.
 * @member:     the name of the list_head within the struct.
 *
 * Continue to iterate over list of given type, continuing after
 * the current position.
 */
#define list_for_each_entry_continue_rcu(pos, head, member)             \
        for (pos = list_entry_rcu(pos->member.next, typeof(*pos), member); \
             &pos->member != (head);    \
             pos = list_entry_rcu(pos->member.next, typeof(*pos), member))

    然后检测dest的flags值,flags值只有两个,具体如下:

// include/uapi/linux/ip_vs.h
/*
 *      Destination Server Flags
 */
#define IP_VS_DEST_F_AVAILABLE  0x0001      /* server is available */
#define IP_VS_DEST_F_OVERLOAD   0x0002      /* server is overloaded */

    IP_VS_DEST_F_AVAILABLE表示服务器可用。
    IP_VS_DEST_F_OVERLOAD表示服务器超载,不可用。
    如果服务器可用,并且权值大于0,那么找到可用服务器,直接跳转到out;
    否则检测list是否已经遍历完(当dest == last时,表示遍历一遍),
        如果已经遍历完,跳转到stop;
        否则继续遍历。
33行:如果pass 小于2,且p 不等于svc->destinations,那么遍历第二遍;
41行:当找到可用服务器时,更新svc->sched_data到当前dest->n_list;
36行,42行解锁。

如果找到可用服务器,返回dest;
否则返回NULL。

WAIT
轮叫调度重点在于取模运算,为什么没有呢???
当找到可用服务器时,会更新svc->sched_data(见41行),它的值为dest->n_list。而我们遍历list时,第一元素是svc->sched_data->n_list(15-16行)。
这里就是取模的实现。

PS:有事,博客先到此为止,有空再补。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值