欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

IPVS调度算法Round-Robin

程序员文章站 2024-03-13 15:38:21
...

函数register_ip_vs_scheduler将RR调度器链接到全局链表ip_vs_schedulers上。具体内容请参见:https://blog.csdn.net/sinat_20184565/article/details/100126417。

static struct ip_vs_scheduler ip_vs_rr_scheduler = {
    .name =                 "rr",                   /* name */
    .n_list =               LIST_HEAD_INIT(ip_vs_rr_scheduler.n_list),
    .init_service =         ip_vs_rr_init_svc,
    .add_dest =             NULL,
    .del_dest =             ip_vs_rr_del_dest,
    .schedule =             ip_vs_rr_schedule,
};

static int __init ip_vs_rr_init(void)
{
    return register_ip_vs_scheduler(&ip_vs_rr_scheduler);
}

虚拟服务接口

在添加IPVS虚拟服务的流程中,绑定调度器函数ip_vs_bind_scheduler时,将首先执行调度器init_service函数指针,对于RR调度器,即函数ip_vs_rr_init_svc。之后执行到虚拟服务的绑定。

static int ip_vs_rr_init_svc(struct ip_vs_service *svc)
{
    svc->sched_data = &svc->destinations;
    return 0;
}

int ip_vs_bind_scheduler(struct ip_vs_service *svc, struct ip_vs_scheduler *scheduler)
{
    if (scheduler->init_service) {
        ret = scheduler->init_service(svc);
        if (ret) {
            pr_err("%s(): init error\n", __func__);
            return ret;
        }
    }
    rcu_assign_pointer(svc->scheduler, scheduler);
}

这里是将虚拟服务所对应的真实服务器链表赋予RR调度器结构的成员sched_data。

真实服务器

RR调度器结构没有实现添加真实服务器的操作指针add_dest,因为在上节的初始化服务指针init_service函数中,RR调度器结构的sched_data成员直接指向了虚拟服务的真实服务器链表。所以只要虚拟服务中的真实服务器链表改变,RR调度器结构就可使用更新后的真实服务器链表。

但是,对于真实服务器的删除操作,需要注意的是,如果删除的是真实服务器链表上的第一个元素,需要更新RR调度器结构的sched_data成员。参见以下的RR调度器函数指针del_dest,即函数ip_vs_rr_del_dest:

static int ip_vs_rr_del_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest)
{
    struct list_head *p;

    spin_lock_bh(&svc->sched_lock);
    p = (struct list_head *) svc->sched_data;

    /* dest is already unlinked, so p->prev is not valid but p->next is valid, use it to reach previous entry.
     */
    if (p == &dest->n_list)
            svc->sched_data = p->next->prev;
    spin_unlock_bh(&svc->sched_lock);
    return 0;
}

调度处理

如下调度函数,虚拟服务svc结构成员sched_data保存有上一次调度的真实服务器结构的链表节点,此次调度将由链表中的下一个真实服务器开始遍历,下一个真实服务器要满足两个条件:

1)不能够是已经过载的服务器;
2)权重值大于零。

另外,遍历pass控制真实服务器链表的遍历次数,整个链表仅完整的遍历一次,因为链表元素由可能会发生改变。

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;

    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)
                goto out;    /* HIT */
            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);

out:
    svc->sched_data = &dest->n_list;

    return dest;
}

如下的添加真实服务器时,分别指定此服务器可接受的最高的连接数量(u-threshold)超过此值将不再接收新的连接;以及低连接阈值(l-threshold),当此真实服务器上的连接数量降低到此值以下后,重新开始接受新的连接。

# ipvsadm -A -t 207.175.44.110:80 -s rr
# ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.1:80 -m --u-threshold 2000 --l-threshold  1000 --weight 200

参数u_threshold的判断逻辑参见以下函数ip_vs_bind_dest:

static inline void ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
{
    if (dest->u_threshold != 0 &&
        ip_vs_dest_totalconns(dest) >= dest->u_threshold)
        dest->flags |= IP_VS_DEST_F_OVERLOAD;
}

参数l-threshold的判断逻辑参见以下函数ip_vs_unbind_dest。如果设置l-threshold等于0,那么当连接数量降低到u_threshold值的四分之三以下后,清除IP_VS_DEST_F_OVERLOAD标志,开始接受新的连接。

static inline void ip_vs_unbind_dest(struct ip_vs_conn *cp)
{
    struct ip_vs_dest *dest = cp->dest; 

    if (dest->l_threshold != 0) {
        if (ip_vs_dest_totalconns(dest) < dest->l_threshold)
            dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
    } else if (dest->u_threshold != 0) {
        if (ip_vs_dest_totalconns(dest) * 4 < dest->u_threshold * 3)
            dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
    } else {
        if (dest->flags & IP_VS_DEST_F_OVERLOAD)
            dest->flags &= ~IP_VS_DEST_F_OVERLOAD;
    }
}

内核版本 4.15

相关标签: ipvs