基于Linux的集群系统(四)集群系统的实现

 

1.设计目标

设计一个基于Linux的集群系统,它能够提供负载平衡的功能。系统能够不断监视集群中各台实际服务器的负载状况,并且将来自外部网的多种请求转发到内部网中的某一台实际服务器上执行。

具体来说,它必须拥有以下的功能:

(1)服务转发。能接受来自外部网中的多种基于TCP/IP的服务请求如FTP 、TELNET、 HTTP等,并且将它们转发到当前负载最轻的机器上执行。

(2)动态负载平衡。平衡器能够监视内部网中的实际服务器的负载状况并且找到负载最轻的机器。

(3)连接持续性。来自外部网的同一客户的所有请求必须转发到内部网中的同一台实际服务器上进行处理。





回页首


2.环境设置

如 图1-1所示,该集群系统由router、server 1、server 2、server 3以及 server n等机器组成。其中router作为内部网和外部网的接口,能够接收外部网的用户请求,并将此用户请求发送到server1到servern中的某台机器 上(如server1),当server1处理完用户的请求以后,就将处理完的结果发送给router,然后再由router将请求回应返回给外部网的用 户。文章中也将router这台机器称为负载平衡器,因为它承担了均衡负载的作用;将实际响应用户请求的server1等机器称为实际服务器。试验环境中 外部网中的客户机器的IP地址为192.9.200.53,负载平衡器(router)有两个IP地址,一个是192.9.200.56, 一个是10.1.1.1,负载平衡器上运行的操作系统内核为Linux 2.2.x。 内部网中有n台实际服务器,它们的IP地址分别为10.1.1.2、10.1.1.3……..10.1.1.n,他们上面运行的操作系统可以是linux 操作系统,也可以是Windows系列的操作系统,如Windows 98、Windows NT等等。它们都将10.1.1.1设为网关,并且都增加了通往192.9.200.0网络的路由。负载平衡的目的就是将客户发向平衡器的请求 如:telnet、 ftp 、www等按照内部网机器当前负载的情况分发到各个实际服务器上。


图1-1 负载平衡系统环境设置图





回页首


3.构建过程

在构建过程这一小节中,首先介绍IP伪装技术的原理,然后介绍集群系统的建立过程。本集群系统的建立首先要建立IP伪装机制,然后再增加IP Portforwarding(IP端口转发)机制,然后设计一个应用程序,它能够根据集群中的机器的状态实现实时动态负载平衡。本集群系统中的负载平衡器上的Linux内核版本为2.2.x。

3.1 IP伪装技术

本集群系统主要采用了IP Masquerade(IP伪装)机制。该负载平衡系统采用了NAT(network address translation)机制。NAT机制主要用于内部私有网与外部网之间进行通讯。IP地址中的那些私有地址如10.0.0.0/255.0.0.0, 172.16.0.0/255.240.0.0 以及192.168.0.0/255.255.0.0等是无法直接与Internet上的机器通讯的,如果它们想与Internet上的机器通讯,需要采用网络地址翻译(Network Address Translation,NAT)机制。

NAT意味着将IP地址从一组映射到另外一组,如果这种映射关系是N-N的,则称之为静态网络地址翻译; 如果映射是M-N(M>N)的,则叫做动态网络地址翻译;IP伪装机制实际上就是一种M-1的动态网络地址翻译,它能够将多个内部网中的IP地址映 射到一个与Internet相连接的外部网IP地址上,这样这些无法直接与Internet上的机器通讯的具有内部网IP地址的机器就可以通过这台映射机 器与外界进行通讯了。而网络地址端口翻译是对网络地址翻译的一种扩展,它将许多网络地址以及它们的TCP/UDP端口翻译为一个IP地址和TCP/UDP 端口。本集群系统采用的就是网络地址端口翻译机制。

3.2 IP伪装机制的建立过程

(1) 编译核心使之能够支持IP伪装。

编译核心时要注意以下选项的选择。

* Prompt for development and/or incomplete code/drivers
(CONFIG_EXPERIMENTAL) [Y/n/?]
- YES: though not required for IP MASQ, this option allows
the kernel to create the MASQ modules and enable the option
for port forwarding
-- Non-MASQ options skipped --
* Enable loadable module support (CONFIG_MODULES) [Y/n/?]
- YES: allows you to load kernel IP MASQ modules
-- Non-MASQ options skipped --
* Networking support (CONFIG_NET) [Y/n/?]
- YES: Enables the network subsystem
-- Non-MASQ options skipped --
* Sysctl support (CONFIG_SYSCTL) [Y/n/?]
- YES: Enables the ability to enable disable options such as forwarding,
dynamic IPs, LooseUDP, etc.
-- Non-MASQ options skipped --
* Packet socket (CONFIG_PACKET) [Y/m/n/?]
- YES: Though this is OPTIONAL, this recommended feature will allow you
to use TCPDUMP to debug any problems with IP MASQ
* Kernel/User netlink socket (CONFIG_NETLINK) [Y/n/?]
- YES: Though this is OPTIONAL, this feature will allow the logging of
advanced firewall issues such as routing messages, etc
* Routing messages (CONFIG_RTNETLINK) [Y/n/?]
- NO: This option does not have anything to do with packet firewall logging
-- Non-MASQ options skipped --
* Network firewalls (CONFIG_FIREWALL) [Y/n/?]
- YES: Enables the kernel to be comfigured by the IPCHAINS firewall tool
* Socket Filtering (CONFIG_FILTER) [Y/n/?]
- OPTIONAL: Though this doesn't have anything do with IPMASQ, if you plan
on implimenting a DHCP server on the internal network, you WILL need this
option.
* Unix domain sockets (CONFIG_UNIX) [Y/m/n/?]
- YES: This enables the UNIX TCP/IP sockets mechanisms
* TCP/IP networking (CONFIG_INET) [Y/n/?]
- YES: Enables the TCP/IP protocol
-- Non-MASQ options skipped --
* IP: advanced router (CONFIG_IP_ADVANCED_ROUTER) [Y/n/?]
- YES: This will allow you to configure advanced MASQ options farther down
* IP: policy routing (CONFIG_IP_MULTIPLE_TABLES) [N/y/?]
- NO: Not needed by MASQ though users who need advanced features
such as TCP/IP source address-based or TOS-enabled routing will
need to enable this option.
* IP: equal cost multipath (CONFIG_IP_ROUTE_MULTIPATH) [N/y/?]
- NO: Not needed for normal MASQ functionality
* IP: use TOS value as routing key (CONFIG_IP_ROUTE_TOS) [N/y/?]
- NO: Not needed for normal MASQ functionality
* IP: verbose route monitoring (CONFIG_IP_ROUTE_VERBOSE) [Y/n/?]
- YES: This is useful if you use the routing code to drop IP
spoofed packets (highly recommended) and you want to log them.
* IP: large routing tables (CONFIG_IP_ROUTE_LARGE_TABLES) [N/y/?]
- NO: Not needed for normal MASQ functionality
* IP: kernel level autoconfiguration (CONFIG_IP_PNP) [N/y/?] ?
- NO: Not needed for normal MASQ functionality
* IP: firewalling (CONFIG_IP_FIREWALL) [Y/n/?]
- YES: Enable the firewalling feature
* IP: firewall packet netlink device
(CONFIG_IP_FIREWALL_NETLINK) [Y/n/?]
- OPTIONAL: Though this is OPTIONAL, this feature will allow
IPCHAINS to copy some packets to UserSpace tools for additional
checks
* IP: transparent proxy support (CONFIG_IP_TRANSPARENT_PROXY) [N/y/?]
- NO: Not needed for normal MASQ functionality
* IP: masquerading (CONFIG_IP_MASQUERADE) [Y/n/?]
- YES: Enable IP Masquerade to re-address specific internal to
external TCP/IP packets
* IP: ICMP masquerading (CONFIG_IP_MASQUERADE_ICMP) [Y/n/?]
- YES: Enable support for masquerading ICMP ping packets
(ICMP error codes will be MASQed regardless). This is an
important feature for troubleshooting connections.
* IP: masquerading special modules support
(CONFIG_IP_MASQUERADE_MOD) [Y/n/?]
- YES: Though OPTIONAL, this enables the OPTION to later enable
the TCP/IP Port forwarding system to allow external computers to
directly connect to specified internal MASQed machines.
* IP: ipautofw masq support (EXPERIMENTAL)
(CONFIG_IP_MASQUERADE_IPAUTOFW) [N/y/m/?]
- NO: IPautofw is a legacy method of port forwarding. It is
mainly old code and has been found to have some issues. NOT
recommended.
* IP: ipportfw masq support (EXPERIMENTAL)
(CONFIG_IP_MASQUERADE_IPPORTFW) [Y/m/n/?]
- YES: Enables IPPORTFW which allows external computers on
the Internet to directly communicate to specified internal
MASQed machines. This feature is typically used to access
internal SMTP, TELNET, and WWW servers. FTP port forwarding
will need an additional patch as described in the FAQ section of
the MASQ HOWTO. Additional information on port forwarding is
available in the Forwards section of this HOWTO.
* IP: ip fwmark masq-forwarding support (EXPERIMENTAL)
(CONFIG_IP_MASQUERADE_MFW) [Y/m/n/?]
- OPTIONAL: This is a new method of doing PORTFW. With this option,
IPCHAINS can mark packets that should have additional work on.
Using a UserSpace tool, much like IPMASQADM or IPPORFW, IPCHAINS
would then automaticaly re-address the packets. Currently, this
code is less tested than PORTFW but it looks promising. For now,
the recommended method is to use IPMASQADM and IPPORTFW. If you
have thoughts on MFW, please email me.
* IP: optimize as router not host (CONFIG_IP_ROUTER) [Y/n/?]
- YES: This optimizes the kernel for the network subsystem though
it isn't known if it makes a siginificant performance difference.
* IP: tunneling (CONFIG_NET_IPIP) [N/y/m/?]
- NO: This OPTIONAL section is for IPIP tunnels through IP Masq.
If you need tunneling/VPN functionality, it is recommended to
use either GRE or IPSEC tunnels.
* IP: GRE tunnels over IP (CONFIG_NET_IPGRE) [N/y/m/?]
- NO: This OPTIONAL selection is to enable PPTP and
GRE tunnels through the IP MASQ box
-- Non-MASQ options skipped --
* IP: TCP syncookie support (not enabled per default)
(CONFIG_SYN_COOKIES) [Y/n/?]
- YES: HIGHLY recommended for basic TCP/IP network security
-- Non-MASQ options skipped --
* IP: Allow large windows (not recommended if <16Mb of memory) *
(CONFIG_SKB_LARGE) [Y/n/?]
- YES: This is recommended to optimize Linux's TCP window
-- Non-MASQ options skipped --
* Network device support (CONFIG_NETDEVICES) [Y/n/?]
- YES: Enables the Linux Network device sublayer
-- Non-MASQ options skipped --
* Dummy net driver support (CONFIG_DUMMY) [M/n/y/?]
- YES: Though OPTIONAL, this option can help when debugging problems
== Don't forget to compile in support for your network card !! ==
-- Non-MASQ options skipped --
== Don't forget to compile in support for PPP/SLIP if you use a modem or
use a PPPoE DSL modem ==
-- Non-MASQ options skipped --
* /proc filesystem support (CONFIG_PROC_FS) [Y/n/?]
- YES: Required to enable the Linux network forwarding system

(2)重新编译了核心以后,应该通过以下命令重新编译并安装IP伪装模块:

make modules; make modules_install

3.3 IP端口转发机制的建立过程

现在需要增加适当的转发机制,从而将数据报文转发到合适的机器上去。首先要注意的是IPFWADM已经不再是2.1.x 和2.2.x核心中控制IP伪装规则的工具,这些核心现在使用的工具是IPCHAINS。

(1)首先根据以下的规则创建/etc/rc.d/rc.firewall文件。

/sbin/depmod -a
/sbin/modprobe ip_masq_ftp
/sbin/modprobe ip_masq_portfw.o
echo "1" > /proc/sys/net/ipv4/ip_forward
echo "1" > /proc/sys/net/ipv4/ip_always_defrag
/sbin/ipchains -M -S 7200 10 160
/sbin/ipchains -P forward DENY
/sbin/ipchains -A forward -s 10.1.1.0/24 -j MASQ
/usr/sbin/ipmasqadm portfw -f
#port forwarding strategy
#port forward the packet of interface 192.9.200.56 to 10.1.1.2 (server2)
#telnet service:port 23
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 23 -R 10.1.1.2 23 -p 1
#ftp service:port 21
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 21 -R 10.1.1.2 21 -p 1
#www service:port 80
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 80 -R 10.1.1.2 80 -p 1
#port forward the packet of interface 192.9.200.56 to 10.1.1.3 (server3)
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 23 -R 10.1.1.3 23 -p 1
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 21 -R 10.1.1.3 21 -p 1
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 80 -R 10.1.1.3 80 -p 1
#port forward the packet of interface 192.9.200.56 to 10.1.1.4 (server4)
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 23 -R 10.1.1.4 23 -p 1
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 21 -R 10.1.1.4 21 -p 1
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 80 -R 10.1.1.4 80 -p 1

(2)在编辑完/etc/rc.d/rc.firewall文件后,运行 chmod 700 /etc/rc.d/rc.firewall命令使该文件变为可执行的。

(3)在 /etc/rc.d/rc.local文件中增加一行来在每次重启以后激活IP伪装模块。

#rc.firewall script - Start IPMASQ and the firewall
/etc/rc.d/rc.firewall.

在依次完成了3.2和3.3小节的操作以后,实际上已经建立起了一个基于Round-Robin调度算法的集群系统,如果用户想根据计算机性能的不同为之 赋相应的权重,只需修改rc.firewall中的规则即可。例如,如果要把server2(10.1.1.2)的权重改为2,只须将原来的规则:

/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 23 -R 10.1.1.2 23 -p 1
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 21 -R 10.1.1.2 21 -p 1
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 80 -R 10.1.1.2 80 -p 1
改为:
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 23 -R 10.1.1.2 23 -p 2
/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 21 -R 10.1.1.2 21 -p 2


/usr/sbin/ipmasqadm portfw -a -P tcp -L 192.9.200.56 80 -R 10.1.1.2 80 -p 2

这样就可以了。

3.4 建立实现动态负载平衡的应用程序

该应用程序监视集群中的各个实际服务器的负载情况,并将用户的请求转发到负载最轻的实际服务器上。具体的实现请参考文章后半部分有关调度模块的实现方法





回页首


4.设计原理

本集群系统实现了IP级的负载平衡。当客户向平衡器发送一个请求报文时,在平衡器的IP层对此请求报文的目标地址进行了替换工作,将目标地址替换为内部网 中的实际服务器中的负载最轻的机器的IP地址。然后将此报文再次转发出去。当内部网中的实际服务器将请求处理完了以后,它将请求回应发向平衡器,平衡器再 次在IP层将目标地址替换为发出请求的外部网中的客户的IP地址,然后将此报文再次转发到客户。

对目标地址进行替换的工作是在操作系统的核心中实现的,而选取负载最轻的机器的IP地址是在应用层实现的。之所以这样做是因为在应用层取负载数据可以提高 系统的可扩展性,当需要向内部网中增加一台新的实际服务器时,只需要在应用程序的数组变量中增加一项就可以了;而且在应用层可以灵活地决定调度策略,可以 采用静态的调度策略如Round Robin、Weighted Round Robin等,也可以采用动态的调度策略如Least Connection 、Weighted Least Connection等。对IP报文进行目标地址改写的工作主要在核心完成,这是因为这样速度很快,省掉了从用户到核心的通讯过程。

当外部网中的客户向负载平衡器发出一个服务请求(如www、ftp、telnet等) 时,从这个请求中可以获得外部网机器的IP地址和端口号(laddr ,lport),以及平衡器的IP地址,根据这些信息查询IP端口转发双向链表看是否有匹配(laddr, lport)的表项存在,如果存在的话,就取出该表项中的(raddr, rport)的值,即内部网中机器的IP地址和端口号,并且替换IP包的目标地址和端口号为(raddr, rport),再将此IP包重新发送到内部网中的对应机器上去。如果没有对应表项,则创建新的IP端口转发表项,以及对应的IP伪装表项。再进行目标地址替换和包重发的工作。





回页首


5.各模块功能

本负载平衡系统主要分为IP伪装模块、IP端口转发模块和调度模块,其中IP伪装模块和IP端口转发模块都是在IP层实现的,在Linux源代码所在目录下都可以找到它们对应的程序。而调度模块是在应用层实现的。

模块名标识符说明
ip伪装模块ip_masq对ip报头进行改写,对ip报文进行转发。
ip端口转发模块ip_portfw 接收外界的请求,根据调度算法决定将ip报文转发到哪一台实际服务器上。
调度模块sched 根据负载信息收集模块确定的负载最轻的机器的地址将用户请求转发到负载最轻的机器上。



6 ip伪装模块的分析

6.1 设计思想

ip伪装模块的主要工作是:

1.接收内部网发向外部网的所有请求。

2.内部网中的连接请求通过平衡器转发到外部网。

3.将内部网发向外部网中的所有请求的源地址隐藏,使所有请求看上去都是由平衡器发送的。

4. 建立HASH表来记录已经建立的所有连接。

5. 接收外部网对请求的回应并将其转发到内部网中的发出请求的机器上。

6.2 模块流程

(1) 内部网中的机器向外部网中的机器发送连接请求的流程。



(2) 外部网中的机器向内部网中的机器发送连接请求的流程。



6.3 结构设计

程序名标识符功能源程序
建立HASH队列ip_masq_hash根据m{addr, port}和s{addr, port}建立两个HASH队列ip_masq.c
从HASH队列删除表项ip_masq_unhash将ip_masq表项从HASH队列中删除ip_masq.c
处理从外到内的请求__ip_masq_in_get处理从外到内的请求,查询HASH表,找到内部机器的ip地址及端口ip_masq.c
处理从内到外的请求__ip_masq_out_get 处理从内到外的请求,查询HASH表,找出源地址、端口及目标地址、端口都匹配的表项。ip_masq.c
减少某个连接的访问计数__ip_masq_put将ip_masq表项的访问计数减一。ip_masq.c
取下一伪装端口get_next_mport取得下一个mport。ip_masq.c
创建新的ip_masq结构ip_masq_new创建一个新ip_masq结构,并分配一个新的mportip_masq.c
从内到外请求处理的最上层函数ip_fw_masquerade负责从内向外请求的处理全过程。ip_masq.c
从外到内请求处理的最上层函数ip_fw_demasquerade负责从外向内请求的处理全过程。ip_masq.c

6.4 主要数据结构

struct  ip_masq{
struct ip_masq *m_link, *s_link;
atomic_t refcnt;
struct timer_list timer;
__u16 protocol;
__u16 sport, dport, mport;
__u32 saddr, daddr, maddr;
struct ip_masq_seq out_seq, in_seq;
void *app_data;
struct ip_masq *control;
atomic_t n_control;
unsigned flags;
unsigned timeout;
unsigned state;
struct ip_masq_timeout_table *timeout_table;
}

6.5 算法及流程

(1) ip_masq_hash

格式:static ip_masq_hash(struct ip_masq *ms)

返回值:1:成功;0:失败。

处理流程:

1. 如果ms->flags 已设IP_MASQ_F_HASHED标志位,则返回0。

2.根据ms->protocol、 ms->maddr和 ms->mport产生hash->key。

3.将ms链接到hash[hash_key] 链的头位置。

4.增加ms的访问计数。

5.根据ms->protocol 、ms->sadd和ms->sport产生hash_key。

6.将ms链接到hash[hash_key] 链的头位置。

7.增加ms的访问计数。

8.ms->flags 设IP_MASQ_F_HASHED位。

9. 返回1。

(2)ip_masq_unhash

格式:static int ip_masq_unhash(struct ip_masq *ms)

返回值: 1:成功;0:失败。

处理流程:

1. 如果ms->flags未设IP_MASQ_F_HASHED位,则返回0。

2. 不然,做以下几步。

3. 根据ms->protocol、 ms->maddr和 ms->mport产生hash->key。

4. 在hash[hash_key]链中找匹配的表项,将匹配项的访问计数减一,并从链表中删除此项。

5. 根据ms->protocol 、ms->sadd和 ms->sport产生hash_key。

6. 在hash[hash_key]链中找匹配的表项,将匹配项的访问计数减一,并从链表中删除此项。

7. ms->flags设位~IP_MASQ_F_HASHED。

8. 返回1。

(3)__ip_masq_in_get

格式:static struct ip_masq *__ip_masq_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)

返回值:返回一个ip_masq结构ms。

处理流程:

1. 根据 protocol 、 d_addr、 d_port产生hash_key。

2. 在hash[hash_key]链中找匹配的表项,满足: (protocol == ms->protocol && d_addr == ms->maddr && dport == ms->mport && (s_addr == ms->daddr || ms->flags & MASQ_DADDR_PASS) && (s_port == ms->dport || ms_flags & MASQ_DPORT_PASS))。

3. ms的访问计数增1。

4. 返回ms。

(4)__ip_masq_out_get

格式:static struct ip_masq *__ip_masq_out_get(int protocol , __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)

返回值:返回一个ms。

处理流程:

1. 如果s_port不为0,做以下几步;不然,跳至第5步。

2. 根据 protocol 、 s_addr和s_port产生hash_key。

3. 在hash[hash_key]链中找匹配的表项,满足:(protocol == ms->protocol && s_addr == ms->saddr && sport == ms->sport && (d_addr == ms->daddr || ms->flags & MASQ_DADDR_PASS) && (d_port == ms->dport || ms_flags & MASQ_DPORT_PASS))。

4. ms的访问计数增1。

5. 返回ms。

6. 根据 protocol 、 s_add、 0产生hash_key。

7. 在hash[hash_key]链中找匹配的表项,满足:(protocol == ms->protocol && s_addr == ms->saddr && sport == ms->sport && (d_addr == ms->daddr || ms->flags & MASQ_DADDR_PASS) && (d_port == ms->dport || ms_flags & MASQ_DPORT_PASS))。

8. ms的访问计数增1。

9. 返回ms。

(5)__ip_masq_getbym

格式:static struct ip_masq *__ip_masq_getbym(int protocol, __u32 m_addr, __u16 m_port)

返回值:返回一个ms。

处理流程:

1. 根据 protocol 、m_add和 m_port产生hash_key。

2. 在hash[hash_key]链中找匹配的表项,满足:(protocol == ms->protocol && m_addr == ms->maddr && mport == ms->mport )。

3. ms的访问计数加1。

4. 返回ms。

(6)ip_masq_out_get

格式:struct ip_masq *ip_masq_out_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)

返回值:返回ms。

处理流程:

1. 为__ip_masq_lock加读锁。

2. 调__ip_masq_out_get(protocol, s_addr, s_port, d_addr, d_port)返回ms。

3. 解读锁__ip_masq_lock。

4. 为ms设置超时。

5. 返回ms。

(7)ip_masq_in_get

格式:struct ip_masq_in_get(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d_port)

返回值:返回ms。

处理流程:

1.为__ip_masq_lock加读锁。

2.调__ip_masq_in_get(protocol, s_addr, s_port, d_addr, d_port)返回ms。

3. 解读锁__ip_masq_lock。

4. 为ms设置超时。

6. 返回ms。

(8)__ip_masq_put

格式:static __inline __void __ip_masq_put(struct ip_masq *ms)

返回值:无

处理流程:

1. 将ms的访问计数减一。

(9)get_next_mport

格式:static __u16 get_next_mport(void)

返回值:返回一个16位的端口号。

处理流程:

1. 为masq_mport_lock加旋转锁。

2. Mport = htons(masq_port ++)。

3. 如果masq_port是最后一个锁,则masq_port = 开始锁。

4. 解旋转锁masq_port_lock。

5. 返回mport。

(10)ip_masq_new

格式:struct ip_masq *ip_masq_new(int proto, __u32 maddr, __u16 mport, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags)

返回值:成功:返回ms;失败:返回null。

处理流程:

1. 如果没有可用端口,返回NULL。

2. 若mflags 设IP_MASQ_F_USER位,则 prio = GFP_KERNEL;不然, prio=GFP_ATOMIC。

3. 为ms分配空间,若失败,返回NULL。

4. MOD_INC_USE_CONUNT。

5. 清空ms。

6. 初始化ms->timer。

7. ms->timer.data = (unsigned long)ms;

ms->timer.function = masq_expire;

ms->protocol = proto;

ms->saddr = saddr;

ms->sport = sport;

ms->daddr = daddr;

ms->dport = dport;

ms->flags = NULL;

ms->app_data = NULL;

ms->control = NULL;

设ms->n_control 为0;

设ms->refcnt 为0;

ms->maddr = maddr;

ms->flags设IP_MASQ_F_NO_REPLY位。

8. 如果((协议不是TCP 和UDP)|| mport),ms的伪装端口设为mport。调用__ip_masq_in_get取得ms。

9. 如果没有匹配的ms,则ms->flags设 IP_MASQ_F_MPORT位;增加伪装端口计数;将ms加入HASH队列;调用ip_masq_bind_app(ms);ms的访问计数加一;为ms的IP_MASQ_S_NONE状态设置超时;返回ms。

10. 如果有匹配项,则调用__ip_masq_put(mst)使访问计数减一;释放ms; MOD_DEC_USE_COUNT;返回NULL。

11. 调用mport = get_next_mport()来找一个可用的端口。

12. 赋值 ms->mport = mport。

13. 调用ip_masq_bind_app(ms)。

14. 设n_fails为0。

15.ms的访问计数增一。

16.为ms的IP_MASQ_S_NONE设置超时。

17.返回ms。

(11)ip_fw_masquerade

格式:int ip_masquerade(struct sk_buff **skb_p, __u32 maddr)

返回值:成功:返回大于0的数;失败:返回-1。

处理流程:

1. 先做一些有关校验和的检查。

2. 调用ip_masq_out_get_iph看入口是否已经存在。

3. 如果入口存在的话,将老的ms从HASH表中删除,将ms->maddr替换为maddr,并将新的ms结构加入HASH队列。

4. 如果没有定义源端口,做以下几步:将老的ms从HASH表中删除,设置ms->sport =h.portp[0],即设置ms的源端口为TCP头中的源端口,将新的ms加入HASH表。

5. 如果定义了IP_MASQ_F_DLOOSE,ms->dport = h .port[1]; ms->daddr = iph->daddr。

6. 如果入口不存在则创建一个新的ms。

7. 将IP头的源地址替换为平衡器的ip地址。

8. 将TCP头的源端口替换为ms的伪装端口。

9. 做一些有关校验和的操作。

10. ms的访问计数减一。

11. 返回0。

(12)ip_fw_demasquerade

格式: int ip_fw_demasquerade(struct sk_buff **skb_p)

返回值:成功:返回1;失败:返回-1。

处理流程:

1. 取得sk_buff中的数据取的起始地址。

2. 取得数据区的大小。

3. 取得针对协议的偏移量。

4. Maddr = iph->daddr。

5. 察看协议类型,如果是ICMP,则调用ip_fw_demasq_icmp;如果是TCP,什么也不做;如果是UDP协议,则首先确认端口号在BEGIN 和END之间,然后对校验和进行一定的操作。

6. 看现有表中是否有匹配项,返回ms。

7. 如果有匹配项,设置标识位~IP_MASQ_F_NO_REPLY;如果定义了非严格路由,则ms->dport = h.portp[0], ms->daddr = iph_saddr。

8. 如果定义了IP_MASQ_F_NO_DPORT,则标识位设~IP_MASQ_F_DPORT, ms的目的端口设为TCP头中的源端口。

9. 如果定义了IP_MASQ_F_NO_DADDR,ms的标识位设~IP_MASQ_F_NO_DADDR,ms的目标地址设为IP头的源地址。

10. 调用masq_skb_cow复制一个skb。

11. 将IP头的目标地址设为ms的源地址。

12. 将TCP头中的目标端口设为ms的源端口。

13. 调用ip_masq_app_pkt_in。

14. 对校验和进行操作。

15. 调用ip_send_check(iph)。

16. 调用masq_set_state(ms, 0 , iph, h.portp )。

17. ms的访问计数减一。

18. 返回1。





回页首


7. ip 端口转发模块的分析

7.1 设计思想

ip端口转发模块的主要工作:

1. 接受外部网的连接请求。

2. 对外只呈现平衡器,使所有请求看起来都是由平衡器处理的。

3. 建立一个端口转发链表。

4. 接收外部网发向内部网的请求。根据连接请求的源地址、源端口、目标地址和目标端口的信息察看链表中是否有对应表项,如果有,则调用ip伪装模块将请求转发 到实际服务器上;如果没有对应表项,则创建一个新的端口转发表项,并且在ip伪装HASH表中增加相应的表项。将实际服务器的处理结果回传给平衡器,再由 平衡器发给外部网中的客户。

5. 在ip端口转发模块中通过系统调用的实现函数对用户的系统调用进行处理,首先清空ip端口转发链表,然后建立对应于最轻负载机器ip地址的ip端口转发表项。

7.2 模块流程



7.3 结构设计

程序名标识符功能源程序
删除表项ip_portfw_del在双向链表中删除一个ip_portfw结构的表项ip_masq_portfw.c
清空双向链表ip_portfw_flush清空双向链表,释放空间ip_masq_portfw.c
寻找匹配表项ip_portfw_lookup寻找符合要求的表项ip_masq_portfw.c
设置权重ip_portfw_edit为匹配的表项设置权重ip_masq_portfw.c
添加表项ip_portfw_add添加一个表项ip_masq_portfw.c
控制双向链表ip_portfw_ctl对双向链表进行控制,进行添加、删除和清空操作ip_masq_portfw.c
匹配表项portfw_in_rule判断是否存在匹配表项ip_masq_portfw.c
创建新入口portfw_in_creat如果没有匹配表项,则创建一个新的ip_masq结构的表项。ip_masq_portfw.c
系统调用处理函数getip 接受来自用户层的系统调用,根据负载最轻的机器的IP值建立新的端口转发链表ip_masq_portfw.c

7.4 主要数据结构

struct  ip_portfw{
struct list_head list;
__u32 laddr, raddr;
__u16 lport, rport;
atomic_t pref_cnt;
int pref;
}
struct list_head{
struct list_head *next, *prev;
}

7.5 算法及流程

(1) ip_portfw_del

格式: static __inline __ int ip_portfw_del(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr)

返回值:如果模块的访问计数 〉0,则返回一个大于0的值,否则返回0;

处理流程:

1. 在双向链表中找到这样的ip_portfw *n 结构,满足:

(n->lport == lport && (!laddr || n->laddr == laddr) && (!raddr || n->raddr == raddr) && (!rport || n->rport == rport )),从链表中删除此入口,释放n结构。

1. 若(&mmod_self -> mmod_nent)为真,则返回ESRCH,否则返回0。

(2) ip_portfw_flush

格式: static __inline__ void ip_portfw_flush(void)

返回值:无。

处理流程:

1. 对于TCP 和UDP链,循环做2. 3步

2. 从链表上删除一个入口,释放结构n。

3. 取下一入口。

(3) ip_portfw_lookup

格式: static __inline__ struct ip_portfw *ip_portfw_lookup(__u16 protocol, __u16 lport, __u32 laddr, __u32 *daddr_p, __u16 *dport_p)

返回值:返回一个ip_portfw结构的变量n。

处理流程:

1.在双向链表中找到这样的ip_portfw *n 结构,满足:

(lport == n->lport && laddr == n->laddr) , 若没有找到,则n = NULL。

2.赋值:*daddr_p = n->raddr;*dport_p = n->rport。

3.返回n。

(4) ip_portfw_edit

格式: static __inline__ int ip_portfw_edit(__u16 protocol, __u16 lport, __u32 laddr, __u16 rport, __u32 raddr, int pref)

返回值:返回满足匹配条件的表项的数目。

处理流程:

1. 设count为0。

2. 在双向链表中找这样的n,满足:( lport == n->lport && (!laddr || laddr == n->laddr) && (!raddr || raddr == n->raddr) && (!rport || rport == n->rport )) 。

3. 为n设置权重。

4. 设n->pref_cnt = pref。

5. 每找到一个匹配项, count都加1。

6. 解读锁portfw_lock。

7. 返回count。

(5) ip_portfw_add

格式: static __inline__ int ip_portfw_add(__u16 protocol, __u16 lport,__u32 laddr, __u16 rport,__u32 raddr, int pref)

返回值:成功:返回0;失败:返回EINVAL。

处理流程:

1. 调用ip_portfw_edit, 若有匹配项,则返回0。

2. 为npf分配一个 struct ip_portfw的空间,若分配失败,则返回"无可用空间" 。

3. MOD_INC_USE_COUNT。

4. npf清0。

5. npf->laddr = laddr;

npf->lport = lport;

npf->rport = rport;

npf->raddr = raddr;

npf->pref = pref;

设npf的访问计数为npf的权重;

6. 调用INIT_LIST_HEAD初始化npf->list。

7. 加写锁&portfw_lock。

8. 调用list_add增加一项到链头。

9. 解写锁。

10. 增加mmod_self计数。

11. 返回0。

(6) portfw_ctl

格式: static __inline__ int portfw_ctl(int optname,struct ip_masq_ctl *mctl, int optlen) 返回值:成功:返回0; 失败:返回EINVAL。

处理流程:

1. 如果cmd为IP_MASQ_CMD_NONE,返回0。

2. 如果cmd为IP_MASQ_CMD_ADD,调用 ip_portfw_add。

3. 如果cmd为IP_MASQ_CMD_DEL,调用 ip_portfw_del。

4. 如果cmd为IP_MASQ_CMD_FLUSH,调用ip_portfw_flush。

5. 返回EINVAL。

(7)portfw_in_creat

格式: static struct ip_masq *portfw_in_creat(const struct sk_buff *skb, const struct iphdr *iph, __u32 maddr)

返回值: 成功:返回ms。

处理流程:

1. 加写锁&portfw_lock。

2. 调用ip_portfw_lookup,若找到匹配项,则调ip_masq_new创建一个新入口表项ms。

3. 调用ip_masq_listen。

4. pf的访问计数减一,若访问计数为0,则设pf的访问计数为pf的权重,将pf 从双向链表中删除,并重新加到链表尾。

5. 返回ms。

(8) 函数getip()

格式:asmlinkage getip(__u32 laddr, __u16 lport, __u32 raddr, __u16 rport) 返回值:无。

处理流程:

1.调用ip_portfw_flush清空ip端口转发链表。

2.调用ip_portfw_add(laddr, lport, raddr, rport, 1)向链表中加入新表项。





回页首


8.调度模块的分析

8.1 设计思想

调度模块的主要任务是:

1. 平衡器向各台实际服务器发送收集负载信息的命令。

2. 各台实际服务器分别运行取cpu运行队列长度的程序。

3. 各台机器将各自的cpu运行队列长度信息回传给平衡器。

4. 平衡器对各台机器的cpu运行队列长度进行比较并选出cpu运行队列长度最短的机器,认为此机器就是负载最轻的机器。

5. 通过系统调用将负载最轻的机器的IP地址传入ip端口转发模块。

8.2 模块流程





8.3 结构设计

程序名功能源程序
client程序收集server方的负载信息并进行处理client.c
server程序收集本机负载信息并传向client方server.c
负载信息收集程序取cpu运行队列长度cpu.c

8.4 主要数据结构

struct info{
char host[15];
char cpu[20];
}

8.5 算法及流程

(1) cpu.c

处理流程:

1. 调用readprottab2取得进程表p_table。

2. 调用do_stat计算 cpu运行队列长度的信息。

(2) 函数do_stat

格式:void do_stat(proc_t **p, float elapsed_time, int pass, int ctl)

处理流程:

1. 设进程状态变量sleeping =0,stopped = 0, zombie = 0, running = 0。

2. 做以下循环:3,4步。

3. 根据进程表取得一个进程,判断它的状态是sleeping、 stopped 、 zombie还是 running,并且给相应的变量sleeping、 stopped 、 zombie、 running加1。

4. 取下一个进程,返回3。

5. 打印正在运行的进程的个数。

(3)client.c

处理流程:

1. 循环做以下步骤:2-11。

2. 设置控制变量i,循环做3-10步。

3. 如果i < NUM(内部网中机器的总数),做以下步骤。

4. 创建套接口sockfd。

5. 调用connect请求建立一个连接。

6. 调用recv将取得的字节流放入缓冲区buf中。

7. 将buf转换为struct info的格式并将转换后的结果赋给load[I]。

8. 比较各机器的cpu运行队列的长度并且确定负载最轻的机器的IP值,以该值为参数通过自定义的系统调用getip进入内核。

9. 关闭套接口sockfd。

8. 睡眠30秒钟。

(4)server.c

流程:

1. 创建套接口sockfd用来侦听。

2. 调用bind进行绑定。

3. 调用listen进行侦听。

4. 循环做以下步骤:5-11。

5. 调用accept接受连接请求。

6. 调用gethostname取得主机名,调用gethostbyname取得主机IP。

7. 将主机IP赋给load->host。

8. 将/loadbalan/cpu.o的运行结果写入./cpuinfo。

9. 打开./cpuinfo文件,将读取到的值写入 load->cpu。

10. 将结构load以字节流的形式发送出去。

11. 关闭套接口。





回页首


9. 系统功能及特点

该负载平衡系统具有以下功能:

(1) 能够对基于TCP/IP协议的多种服务如telnet、ftp、 http等进行转发。在应用程序中,用户可以通过加入特定服务对应的端口号(如ftp对应的端口号是21)来增加对特定的服务的支持。

(2) 实现了动态负载平衡。负载平衡器不断收集各台实际服务器正在运行的进程的个数,通过比较找出具有最少运行进程个数的机器,并且将请求发向这台机器。这种负载平衡是随各台机器现有的负载情况的变化而变化的,因此是动态的负载平衡。

(3) 保证了持续连接。当来一个新的请求时,负载平衡器查找已建立连接的表项来看是否有匹配表项,如果发现已有匹配项,则将请求发往对应的实际服务器,这样就保证了来自同一个用户的同一个服务的多次请求能发送到同一台实际服务器上。

该负载平衡器具有以下的性能特点:

(1) 具有较短的响应时间。从负载平衡器向集群中实际服务器发出收集负载的请求到决定出负载最轻的机器所用的总时间约为30ms,该负载平衡系统能够保证毫秒级的响应时间。

(2) 具有良好的可伸缩性。用户可以在应用程序中对集群中的机器个数进行控制,程序中有一个数组来记录机器的IP地址,因此增加一台机器只需要向数组中增加一个新的元素,删除一台机器只需要从数组中删除对应的元素。

(3) 具有良好的容错性。当集群中的某台机器突然崩溃时,应用程序能够立即发现,并且不会再将请求分发给失效的机器;当机器修复了错误重新进入集群中时,应用程序能够探测到并开始进行正常的负载收集工作。



参考资料




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值