基於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結構,並分配一個新的mport ip_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) 具有良好的容錯性。當集羣中的某臺機器突然崩潰時,應用程序能夠立即發現,並且不會再將請求分發給失效的機器;當機器修復了錯誤重新進入集羣中時,應用程序能夠探測到並開始進行正常的負載收集工作。



參考資料


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章