LVS/Nginx如何處理session問題
原創(chuàng)【51CTO.com獨家特稿】業(yè)務系統架構為:
拓補一:Nginx(master)+keepalived+Nginx(backup)+3臺web集群+mysql(master-slave)+EMC CLARiiON CX4存儲
拓補二:lvs(master)+keepalived+lvs(backup) +3臺web集群+mysql(master-slave)+EMC CLARiiON CX4存儲
操作系統用的是64位RHEl5.4/Centos5.4,服務器采用HP360G6+HP580G5,業(yè)務系統最前端的防火墻為華賽USG5000+WAF-T3-500(防DDOS、釣魚式及注入式攻擊等)
拓補一中,如采用Nginx負載均衡器,采用的ip_hash來代替默認的rr方式,即可以將某客戶端IP的請求通過哈希算法定位到同一臺后端web服務器上,這樣避免了session丟失,解決了session問題。但ip_hash指令無法保證后端服務器的負載均衡,可能有些后端服務器接收的請求多,有些后端服務器接收的請求少;這樣失去了負載均衡的意義。我們的解決方案是將用戶的登錄session信息寫進后端的Mysql數據庫,這個在后面的CMS系統中也實現了,效果也不錯;后來我提出了折衷方案,如果Nginx并發(fā)連接數(即Nginx負載均衡器的NginxStatus的active connections)>2000,即采用將session寫進MySQL數據庫的方法;如果并發(fā)數小的話,ip_hash效果也是相當好的。
另外,如果在upstream中添加了ip_hash參數后,經測試發(fā)現后臺的某臺服務器掛掉后不會自動跳轉,可建議采用如下寫法:
- upstream njzq.com {
- ip_hash;
- server 172.16.94.216:9000 max_fails=0;
- server 172.16.94.217:9000 max_fails=0;
- server 172.16.94.218:9000 max_fails=0;
- }
拓補二中,lvs采用的ipvsadm -p方案,persistence-會話保持時間,單位是秒。我一般是設為120s,這個選項對動態(tài)網站很有用處:當用戶從遠程用帳號進行登陸網站時,有了這個會話保持功能,就能把用戶的請求轉發(fā)給同一個應用服務器。當用戶第一次訪問的時候,他的訪問請求被負載均衡器轉給某個真實服務器,這樣他看到一個登陸頁面,第一次訪問完畢;接著他在登陸框填寫用戶名和密碼,然后提交;這時候,問題就可能出現了—登陸不能成功。因為沒有會話保持,負載均衡器可能會把第2次的請求轉發(fā)到其他的服務器。那么設置后是不是前面的客戶機跟后面的服務器都永遠建議連接關系呢,蠻或是過了120秒后或切換到另一臺真實的物理服務器呢?我嘗試作了以下實驗,lvs采用單臺,192.168.1.102,VIP為192.168.1.188,后端為二臺web服務器,192.168.1.103和192.168.1.104。
lvs上面執(zhí)行下列腳本,二臺真實的服務器下也要執(zhí)行相關腳本,綁定vip地址192.168.1.188;lvs和真實物理服務器上分別使用lvs_dr.sh和real.sh腳本
- [root@ltos lvs]# cat lvs_dr.sh
- #!/bin/bash
- # website director vip.
- SNS_VIP=192.168.1.188
- SNS_RIP1=192.168.1.103
- SNS_RIP2=192.168.1.104
- . /etc/rc.d/init.d/functions
- logger $0 called with $1
- case "$1" in
- start)
- # set squid vip
- /sbin/ipvsadm --set 30 5 60
- /sbin/ifconfig eth0:0 $SNS_VIP broadcast $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP up
- /sbin/route add -host $SNS_VIP dev eth0:0
- /sbin/ipvsadm -A -t $SNS_VIP:80 -s wlc -p 120
- /sbin/ipvsadm -a -t $SNS_VIP:80 -r $SNS_RIP1:80 -g -w 1
- /sbin/ipvsadm -a -t $SNS_VIP:80 -r $SNS_RIP2:80 -g -w 1
- touch /var/lock/subsys/ipvsadm >/dev/null 2>&1
- ;;
- stop)
- /sbin/ipvsadm -C
- /sbin/ipvsadm -Z
- ifconfig eth0:0 down
- route del $SNS_VIP
- rm -rf /var/lock/subsys/ipvsadm >/dev/null 2>&1
- echo "ipvsadm stoped"
- ;;
- status)
- if [ ! -e /var/lock/subsys/ipvsadm ];then
- echo "ipvsadm stoped"
- exit 1
- else
- echo "ipvsadm OK"
- fi
- ;;
- *)
- echo "Usage: $0 {start|stop|status}"
- exit 1
- esac
- exit 0
二臺web真實物理服務器運行real.sh腳本
- #!/bin/bash
- SNS_VIP=192.168.1.188
- . /etc/rc.d/init.d/functions
- case "$1" in
- start)
- ifconfig lo:0 $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP
- /sbin/route add -host $SNS_VIP dev lo:0
- echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore
- echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce
- echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore
- echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce
- sysctl -p >/dev/null 2>&1
- echo "RealServer Start OK"
- ;;
- stop)
- ifconfig lo:0 down
- route del $SNS_VIP >/dev/null 2>&1
- echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore
- echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce
- echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore
- echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce
- echo "RealServer Stoped"
- ;;
- *)
- echo "Usage: $0 {start|stop}"
- exit 1
- esac
- exit 0
通過觀察得知,當 客戶機192.168.1.100發(fā)起第一次連接請求時,lvs負載均衡器將其分配到后面的真實物理服務器192.168.1.104,在完成了三次握手后,連接的狀態(tài)為ESTABLISHED,隨后在終止TCP連接的相當一段時間內,真實web服務器的狀態(tài)為FIN_WAIT,而在此段時間192.168.1.100發(fā)起的新連接,會一直連接到192.168.1.104。
附注:動態(tài)網站即指有PHP登陸的,如果后端是緩存集群,這個會話選項可嘗試去除;不過我用的CDN中都是用F5硬件,目前暫時還沒機會測試。
在項目實施中,我跟同事們交流習慣將整個系統構架分成三層,即:負載均衡層、web層和數據庫層;發(fā)現大家都喜歡說集群這個概念,我感覺這個概念混淆了,雖然我知道他們指的是lvs這塊,我更喜歡用負載均衡這個專業(yè)術語;負載均衡器即我上面提到的Nginx/lvs等,它們能將客戶端的請求根據不同算法,分配到后端的服務器集群,比如apache、tomcat、squid集群等;高可用是將最前端的負載均衡器作failover,即在很短時間(<1s)將備機替換出故障機器,目前成熟的負載均稀高可用架構有l(wèi)vs+keepalived、nginx+keepalived(heartbeat我主要用于內網開發(fā)環(huán)境,暫未投入生產環(huán)境);如果非要說成集群,我建議說成linux集群,這樣大家一聽就知道是lvs環(huán)境,如果以上說法或配置有誤,煩請大家通知51CTO編輯或者作者撫琴煮酒yuhongchun027@163.com,我們會在第一時間更正,以免誤導讀者。
【51CTO.com獨家特稿,非經授權謝絕轉載,合作媒體轉載請注明原文作者及出處!】