前言
在做負載均衡集群的時候,如果后端是應用服務器,我們就有一個不得不考慮的一個問題:會話綁定。為了追蹤會話,我們常見的有三種方式:
(1)session sticky
:會話粘性,常見有2種方式:
-
source_ip
:采用源地址綁定方式 -
nginx:ip_hash,ip地址哈希
-
haproxy:source
-
lvs:sh,源地址哈希
-
cookie
:基于cookie綁定 -
nginx:hash
-
haproxy:cookie
(2)session cluster
:session集群
(3)session server
:session服務器,結合memcache或redis。
下面以Tomcat為例,來分別演示一下。
Tomcat Session Cluster結合httpd作為反代有如下三種實現方式:
Tomcat Cluster (1) httpd + tomcat cluster httpd: mod_proxy, mod_proxy_http, mod_proxy_balancer tomcat cluster:http connector (2) httpd + tomcat cluster httpd: mod_proxy, mod_proxy_ajp, mod_proxy_balancer tomcat cluster:ajp connector (3) httpd + tomcat cluster httpd: mod_jk tomcat cluster:ajp connector
實驗步驟
在node3和node4上安裝部署tomcat
[root@node3 ~]# yum install java-1.8.0-openjdk-devel tomcat-lib.noarch tomcat-admin-webapps.noarch tomcat-webapps.noarch -y
創建測試頁面
:
[root@node4 ~]# mkdir -pv /usr/share/tomcat/webapps/test/{WEB-INF,lib,classes} mkdir: created directory ‘/usr/share/tomcat/webapps/test’ mkdir: created directory ‘/usr/share/tomcat/webapps/test/WEB-INF’ mkdir: created directory ‘/usr/share/tomcat/webapps/test/lib’ mkdir: created directory ‘/usr/share/tomcat/webapps/test/classes’ [root@node4 ~]#
為了演示效果,node3上測試頁面內容如下:
<%@ page language="java" %> <html> <head><title>Tomcat:Node3</title></head> <body> <h1><font color="red">Tomcat:Node3</font></h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("magedu.com","magedu.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
為了演示效果,node4上測試頁面內容如下:
<%@ page language="java" %> <html> <head><title>Tomcat:Node4</title></head> <body> <h1><font color="blue">Tomcat:Node4</font></h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("magedu.com","magedu.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
啟動tomcat,訪問測試:
1)用nginx反代:session sticky(ip_hash)的實現
在node2上安裝nginx,配置:
在/etc/nginx/nginx.conf中的http上下文中定義服務器組
upstream tcsrvs { server 172.16.47.103:8080; server 172.16.47.104:8080; }
在/etc/nginx/conf.d/default.conf中使用代理
location / { root /usr/share/nginx/html; proxy_pass http://tcsrvs/; index index.html index.htm; }
啟動nginx,瀏覽器訪問node2的ip:
可以看到,負載均衡功能已經實現了,但是Session ID一直在變,session綁定問題還沒決解決。
要想基于session sticky
綁定,我們可以做ip_hash
,在upstream
段中加入ip_hash
,再來訪問:
可以看到,session id一直保持不變,會話綁定的問題解決了,但是用戶也被綁定到一臺主機上了。
2)用httpd反代:session sticky(cookie)
停掉nginx,在node2上安裝httpd:yum install httpd -y
配置httpd,讓其負載均衡node3和node4,要用到mod Proxy balancer模塊,要確保該模塊存在,可以使用httpd -M,查看
<proxy balancer://tcsrvs> BalancerMember http://172.16.47.103:8080 BalancerMember http://172.16.47.104:8080 ProxySet lbmethod=byrequests </Proxy> <VirtualHost *:80> ServerName lb.magedu.com ProxyVia On ProxyRequests Off ProxyPreserveHost On <Proxy *> Require all granted </Proxy> ProxyPass / balancer://tcsrvs/ ProxyPassReverse / balancer://tcsrvs/ <Location /> Require all granted </Location> </VirtualHost>
啟動httpd,訪問node2,可以看到,同nginx一樣,實現了負載均衡,但是session問題沒解決
要實現會話的綁定,修改配置如下:
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED <proxy balancer://tcsrvs> BalancerMember http://172.16.47.103:8080 route=Tomcatnode3 loadfactor=1 BalancerMember http://172.16.47.104:8080 route=Tomcatnode4 loadfactor=2 ProxySet lbmethod=byrequests ProxySet stickysession=ROUTEID </Proxy> <VirtualHost *:80> ServerName lb.magedu.com ProxyVia On ProxyRequests Off ProxyPreserveHost On <Proxy *> Require all granted </Proxy> ProxyPass / balancer://tcsrvs/ ProxyPassReverse / balancer://tcsrvs/ <Location /> Require all granted </Location> </VirtualHost>
可以看到會話被綁定了。如果要是用ajp的話,只需要把http改為ajp就可以,但是要確保ajp模塊被加載。
另外,proxy balancer有一個web管理界面,類似于tomcat的gui界面,如果要啟用的話,需要加入以下配置:
<Location /balancer-manager> SetHandler balancer-manager ProxyPass ! Require all granted </Location>
使用session sticky會有很多問題,它是通過把會話與主機綁定來實現會話保持的,但如果某一臺主機掛了,該主機上用戶的session也就丟失了,所以我們可以采用session cluster來保存用戶會話。
3)session cluster
正常情況下,session信息是保存在用戶所訪問的服務器上的,服務器掛了,用戶的session也就丟失了,但是我們可以通過session cluster的方式來實現將用戶的session信息保存在后端的所有服務器上,這樣,無論用戶的請求被調度至哪一臺服務器,session都不會丟失。
四種常見的session manager
-
StandardManager
: -
PersistentManager
: 可以將session信息保存在持久存儲中 -
DeltaManager
: 將session信息通過多播的形式共享到其他節點 -
BackupManager
: 將session信息共享到特定的一個節點上
我們使用DeltaManager方式來配置:
以下配置可以放在host中,也可以放在engine中,生效范圍不同
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" mapSendOptions="6"/> <!-- <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> --> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.47.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="172.16.47.104" port="5000" selectorTimeout="100" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/> <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster> 注意:<Engine name="Catalina" defaultHost="localhost" jvmRoute="Tomcatnode4" >,這個jvmRoute必須得配置
然后執行如下操作,注意,node3,4上都要執行。
[root@node4 /usr/share/tomcat/webapps]# cp /etc/tomcat/web.xml test/WEB-INF/ vim test/WEB-INF/web.xml ,加入如下配置。 <distributable/>
重啟tomcat,通過日志tail -f /var/log/tomcat/catalina.2017-02-09.log ,可以看到cluster的工作過程,這里就不截圖了
利用上面nginx的配置,不過要去掉ip_hash,我們啟動nginx,然后瀏覽器訪問:
可以看到被調度到不同主機上了,但是Session ID一直沒有變,這樣,我們就實現了session cluster的功能。
總結
這篇文章實現了tomcat session 保持的各種方法,但是實際上這些方式用的并不多,試想當后端tomcat服務器很多的情況下,它們要一直多播通信,在用戶高并發的情況下,很可能會造成網絡擁堵。所以在實際生產環境中的解決方法是做session server,我們下篇文章再演示。
原創文章,作者:Lurker,如若轉載,請注明出處:http://www.www58058.com/68266