LVS/Nginx如何处理session问题

时间:2022-11-18 03:09:48

业务系统架构为:

拓补一: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,业务系统最前端的防火墙为华赛USG5000+WAF-T3-500(防DDOS、钓鱼式及注入式攻击等)

拓补一中,如采用Nginx负载均衡器,采用的ip_hash来代替默认的rr方式,即可以将某客户端IP的请求通过哈希算法定位到同一台后端web服务器上,这样避免了session丢失,解决了session问题。但ip_hash指令无法保证后端服务器的负载均衡,可能有些后端服务器接收的请求多,有些后端服务器接收的请求少;这样失去了负载均衡的意义。我们的解决方案是将用户的登录session信息写进后端的Mysql数据库,这个在后面的CMS系统中也实现了,效果也不错;后来我提出了折衷方案,如果Nginx并发连接数(即Nginx负载均衡器的NginxStatus的active connections)>2000,即采用将session写进MySQL数据库的方法;如果并发数小的话,ip_hash效果也是相当好的。

另外,如果在upstream中添加了ip_hash参数后,经测试发现后台的某台服务器挂掉后不会自动跳转,可建议采用如下写法:

  1. upstream  njzq.com  {  
  2.      ip_hash;    
  3.      server   172.16.94.216:9000 max_fails=0;  
  4.      server   172.16.94.217:9000 max_fails=0;  
  5.      server   172.16.94.218:9000 max_fails=0;  

拓补二中,lvs采用的ipvsadm -p方案,persistence-会话保持时间,单位是秒。我一般是设为120s,这个选项对动态网站很有用处:当用户从远程用帐号进行登陆网站时,有了这个会话保持功能,就能把用户的请求转发给同一个应用服务器。当用户第一次访问的时候,他的访问请求被负载均衡器转给某个真实服务器,这样他看到一个登陆页面,第一次访问完毕;接着他在登陆框填写用户名和密码,然后提交;这时候,问题就可能出现了—登陆不能成功。因为没有会话保持,负载均衡器可能会把第2次的请求转发到其他的服务器。那么设置后是不是前面的客户机跟后面的服务器都永远建议连接关系呢,蛮或是过了120秒后或切换到另一台真实的物理服务器呢?我尝试作了以下实验,lvs采用单台,192.168.1.102,VIP为192.168.1.188,后端为二台web服务器,192.168.1.103和192.168.1.104。

lvs上面执行下列脚本,二台真实的服务器下也要执行相关脚本,绑定vip地址192.168.1.188;lvs和真实物理服务器上分别使用lvs_dr.sh和real.sh脚本

  1. [root@ltos lvs]# cat lvs_dr.sh   
  2.  #!/bin/bash  
  3.            
  4.          # website director vip.  
  5.          SNS_VIP=192.168.1.188  
  6.          SNS_RIP1=192.168.1.103  
  7.          SNS_RIP2=192.168.1.104  
  8.  
  9.       . /etc/rc.d/init.d/functions  
  10.  
  11.          logger $0 called with $1  
  12.  
  13.          case "$1" in  
  14.  
  15.          start)  
  16.          # set squid vip  
  17.          /sbin/ipvsadm --set 30 5 60  
  18.          /sbin/ifconfig eth0:0 $SNS_VIP broadcast $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP up  
  19.          /sbin/route add -host $SNS_VIP dev eth0:0  
  20.          /sbin/ipvsadm -A -t $SNS_VIP:80 -s wlc -p 120  
  21.          /sbin/ipvsadm -a -t $SNS_VIP:80 -r $SNS_RIP1:80 -g -w 1  
  22.          /sbin/ipvsadm -a -t $SNS_VIP:80 -r $SNS_RIP2:80 -g -w 1  
  23.          touch /var/lock/subsys/ipvsadm >/dev/null 2>&1  
  24.  
  25.         ;;  
  26. stop)  
  27.          /sbin/ipvsadm -C  
  28.          /sbin/ipvsadm -Z  
  29.          ifconfig eth0:0 down  
  30.          route del $SNS_VIP  
  31.          rm -rf /var/lock/subsys/ipvsadm >/dev/null 2>&1  
  32.          echo "ipvsadm stoped"  
  33.         ;;  
  34.  
  35. status)  
  36.  
  37.          if [ ! -e /var/lock/subsys/ipvsadm ];then  
  38.                  echo "ipvsadm stoped"  
  39.                  exit 1  
  40.          else  
  41.                  echo "ipvsadm OK"  
  42.          fi  
  43.        ;;  
  44.  
  45. *)  
  46.          echo "Usage: $0 {start|stop|status}"  
  47.          exit 1  
  48. esac  
  49. exit 0 

二台web真实物理服务器运行real.sh脚本

  1. #!/bin/bash  
  2. SNS_VIP=192.168.1.188  
  3. . /etc/rc.d/init.d/functions  
  4.  
  5. case "$1" in  
  6. start)  
  7. ifconfig lo:0 $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP  
  8. /sbin/route add -host $SNS_VIP dev lo:0  
  9. echo "1" >/proc/sys/net/ipv4/conf/lo/arp_ignore  
  10. echo "2" >/proc/sys/net/ipv4/conf/lo/arp_announce  
  11. echo "1" >/proc/sys/net/ipv4/conf/all/arp_ignore  
  12. echo "2" >/proc/sys/net/ipv4/conf/all/arp_announce  
  13. sysctl -p >/dev/null 2>&1  
  14. echo "RealServer Start OK"  
  15. ;;  
  16. stop)  
  17. ifconfig lo:0 down  
  18. route del $SNS_VIP >/dev/null 2>&1  
  19. echo "0" >/proc/sys/net/ipv4/conf/lo/arp_ignore  
  20. echo "0" >/proc/sys/net/ipv4/conf/lo/arp_announce  
  21. echo "0" >/proc/sys/net/ipv4/conf/all/arp_ignore  
  22. echo "0" >/proc/sys/net/ipv4/conf/all/arp_announce  
  23. echo "RealServer Stoped"  
  24. ;;  
  25. *)  
  26. echo "Usage: $0 {start|stop}"  
  27. exit 1  
  28. esac  
  29. exit 0 

通过观察得知,当 客户机192.168.1.100发起第一次连接请求时,lvs负载均衡器将其分配到后面的真实物理服务器192.168.1.104,在完成了三次握手后,连接的状态为ESTABLISHED,随后在终止TCP连接的相当一段时间内,真实web服务器的状态为FIN_WAIT,而在此段时间192.168.1.100发起的新连接,会一直连接到192.168.1.104。

附注:动态网站即指有PHP登陆的,如果后端是缓存集群,这个会话选项可尝试去除;不过我用的CDN中都是用F5硬件,目前暂时还没机会测试。

在项目实施中,我跟同事们交流习惯将整个系统构架分成三层,即:负载均衡层、web层和数据库层;发现大家都喜欢说集群这个概念,我感觉这个概念混淆了,虽然我知道他们指的是lvs这块,我更喜欢用负载均衡这个专业术语;负载均衡器即我上面提到的Nginx/lvs等,它们能将客户端的请求根据不同算法,分配到后端的服务器集群,比如apache、tomcat、squid集群等;高可用是将最前端的负载均衡器作failover,即在很短时间(<1s)将备机替换出故障机器,目前成熟的负载均稀高可用架构有lvs+keepalived、nginx+keepalived(heartbeat我主要用于内网开发环境,暂未投入生产环境);如果非要说成集群,我建议说成linux集群,这样大家一听就知道是lvs环境,如果以上说法或配置有误,烦请大家通知51CTO编辑或者作者抚琴煮酒yuhongchun027@163.com,我们会在第一时间更正,以免误导读者。

【51CTO.com独家特稿,非经授权谢绝转载,合作媒体转载请注明原文作者及出处!】