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
/*
* 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: <proto, caddr, 0, vaddr, vport, daddr, dport> */
//找到已经创建的模板,主要用于将同一客户端重定向至同一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: <proto, caddr, 0, vaddr, 0, daddr, 0>
* 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);
}