dpvs persistent调度

  • ipvsadmin配置命令,开启persistent选项之后,ipvs将来自同一个客户端的请求全部调度到一个固定的RS服务器上。对于SSL和FTP这类,其多个报文之间是相互关联的协议,需要开启此类功能。但是对于NAT转发模式,由于NAT将对端口号进行修改,FTP服务需要使用ip_vs_ftp模块才能正常工作

  • 命令行选项netmask默认为255.255.255.255,仅对同一个客户端进行持续调度。如下指定掩码255.255.255.0,可对同一个网段内的所有客户端,调度到同一个目的服务器进行处理

    ./ipvsadm -A -t 207.175.44.110:80 -s rr --persistent 500 --netmask 255.255.255.0
    
  • dp_vs_schedule

    struct dp_vs_conn *dp_vs_schedule(struct dp_vs_service *svc,
                                      const struct dp_vs_iphdr *iph,
                                      struct rte_mbuf *mbuf,
                                      bool is_synproxy_on,
                                      bool outwall)
    {
    		....
    		if (svc->flags & DP_VS_SVC_F_PERSISTENT)
        {
            return(dp_vs_sched_persist(svc, iph, mbuf, is_synproxy_on));
        }
    		....
    }
    
  • dp_vs_sched_persist

    • 首先根据客户端地址&掩码与操作,可实现将一个网段内的所有客户端调度到同一个目的服务器
    • 查找模板连接,查找到则将dest设置为模板连接的dest,保证同一客户端调度到同一目的服务器,如果未查找到新建模板连接
    • 根据模板连接信息,新建该tcp的连接
    • 设置新conn关联的control实例,并添加control的引用计数,在连接清理处可以看到
    /*
     * IPVS persistent scheduling funciton.
     * It create a connection entry according to its template if exists,
     * or selects a server and creates a connection entry plus a template.
     */
    static struct dp_vs_conn *dp_vs_sched_persist(struct dp_vs_service *svc,
                                                  const struct dp_vs_iphdr *iph, struct rte_mbuf *mbuf, bool is_synproxy_on)
    {
        uint32_t                conn_flags;
        uint16_t                _ports[2], *ports;
        uint16_t                dport;
        struct dp_vs_dest *     dest;
        struct dp_vs_conn *     conn, *ct;
        struct dp_vs_conn_param param;
        union inet_addr         snet; /* source network of eth client after masking */
    
    #ifdef CONFIG_DPVS_IPVS_DEBUG
        char sbuf[64], dbuf[64], maskbuf[64];
    #endif
    
        assert(svc && iph && mbuf);
    
        conn_flags = (is_synproxy_on ? DPVS_CONN_F_SYNPROXY : 0);
        if (svc->af == AF_INET6)
        {
            /* FIXME: Is OK to use svc->netmask as IPv6 prefix length ? */
            ipv6_addr_prefix_copy(&snet.in6, &iph->saddr.in6, svc->netmask);
        }
        else
        {
            //ipvsadm设置的时候,可以通过netmask对一个网段的客户端进行调度,首先获取数据包中ip源地址与设置网段掩码的与操作,
            //用于网段进行匹配。客户端地址和掩码的与操作,可实现将一个网段内的所有客户端调度到同一个目的服务器上
            snet.in.s_addr = iph->saddr.in.s_addr & svc->netmask;
        }
        //获取端口的偏移
        ports = mbuf_header_pointer(mbuf, iph->len, sizeof(_ports), _ports);
        if (!ports)
        {
            return(NULL);
        }
    
    #ifdef CONFIG_DPVS_IPVS_DEBUG
        RTE_LOG(DEBUG, IPVS, "%s: persist-schedule: src %s/%u dest %s/%u snet %s\\n",
                __func__,
                inet_ntop(svc->af, &iph->saddr, sbuf, sizeof(sbuf)),
                ntohs(ports[0]),
                inet_ntop(svc->af, &iph->daddr, dbuf, sizeof(dbuf)),
                ntohs(ports[1]),
                inet_ntop(svc->af, &snet, maskbuf, sizeof(maskbuf)));
    #endif
        //如果配置了DPVS虚拟服务的指定服务端口号,则根据端口号是否为服务端口,创建的连接模板有所不同。
        if (ports[1] == svc->port)
        {
            /* regular persistent service:  */
            //找到已经创建的模板,主要用于将同一客户端重定向至同一rs
            ct = dp_vs_ct_in_get(svc->af, iph->proto, &snet, &iph->daddr, 0, ports[1]);
            if (!ct || !dp_vs_check_template(ct))
            {
                /* no template found, or the dest of the conn template is not available */
                //如果没有指定当前报文的连接模板,首先创建连接模板,再创建相应的连接结构。后续的报文,如果匹配此模板,将据此
                //模板生成连接结构,而不再执行调度,这样确保了根据模板生成的连接,具有相同的目的服务器,以实现持续调度的功能
                dest = svc->scheduler->schedule(svc, mbuf);
                if (unlikely(NULL == dest))
                {
                    RTE_LOG(WARNING, IPVS, "%s: persist-schedule: no dest found.\\n", __func__);
                    return(NULL);
                }
                /* create a conn template */
                dp_vs_conn_fill_param(iph->af, iph->proto, &snet, &iph->daddr,
                                      0, ports[1], 0, ¶m);
                //创建新的模板连接,设置DPVS_CONN_F_TEMPLATE标识是一个模板
                ct = dp_vs_conn_new(mbuf, iph, ¶m, dest, conn_flags | DPVS_CONN_F_TEMPLATE);
                if (unlikely(NULL == ct))
                {
                    return(NULL);
                }
    
                ct->timeout.tv_sec = svc->timeout;
            }
            else
            {
                /* set destination with the found template */
                //如果找到,则该客户端之前有过连接,选取与之前的连接相同的rs,设置dest
                dest = ct->dest;
            }
            dport = dest->port;
        }
        else
        {
            /* port zero service: 
             * fw-mark based service: not support */
             //如果未指定服务端口,此处查找时忽略目的端口
            ct = dp_vs_ct_in_get(svc->af, iph->proto, &snet, &iph->daddr, 0, 0);
            if (!ct || !dp_vs_check_template(ct))
            {
                dest = svc->scheduler->schedule(svc, mbuf);
                if (unlikely(NULL == dest))
                {
                    RTE_LOG(WARNING, IPVS, "%s: persist-schedule: no dest found.\\n", __func__);
                    return(NULL);
                }
                /* create a conn template */
                dp_vs_conn_fill_param(iph->af, iph->proto, &snet, &iph->daddr,
                                      0, 0, 0, ¶m);
    
                ct = dp_vs_conn_new(mbuf, iph, ¶m, dest, conn_flags | DPVS_CONN_F_TEMPLATE);
                if (unlikely(NULL == ct))
                {
                    return(NULL);
                }
    
                ct->timeout.tv_sec = svc->timeout;
            }
            else
            {
                /* set destination with the found template */
                dest = ct->dest;
            }
            dport = ports[1];
        }
    
        /* create a new connection according to the template */
        //根据上面找的连接模板,选择了dest后,创建连接
        dp_vs_conn_fill_param(iph->af, iph->proto, &iph->saddr, &iph->daddr,
                              ports[0], ports[1], dport, ¶m);
    
        conn = dp_vs_conn_new(mbuf, iph, ¶m, dest, conn_flags);
        if (unlikely(NULL == conn))
        {
            dp_vs_conn_put(ct);
            return(NULL);
        }
    
        /* add control for the new connection */
        //设置新建conn的control为模板连接,并添加模板连接的引用计数
        dp_vs_control_add(conn, ct);
        dp_vs_conn_put(ct);
    
        dp_vs_stats_conn(conn);
    
        return(conn);
    }
    

你可能感兴趣的:(dpvs persistent调度)