以前文章已經描述了企業高可用負載相關的架構及實現,其中經常使用的nginx或haproxy,LVS結合keepalived作前端高可用調度器;但以前沒有提到會話高可用保持;
本文經過 Tomcat Session Replication Cluster(tomcat自帶)和tomcat結合memcat及第三方組件實現Tomcat Memcache Session Server高可用會話緩存服務;
實現的效果:
同一客戶端訪問業務網站,通過調度器負載調度到達後端,無論選擇的是那個後端,session ID都不變,都保存在兩臺或多臺的memcached緩存中(負載冗餘);以保持持會話;css
架構圖:
說明:客戶端請求時nginx經過負載調度算法將請求調度至某一後端服務器;tomcat 把會話經過組播的方式複製到集羣各節點;全部節點共享會話;html
說明:客戶端請求時nginx經過負載調度算法將請求調度至某一後端服務器;並把session存儲到兩臺memcached中;客戶端刷新(不換瀏覽器)時,請求換成另外一個後端服務器響應時session ID保持不變;前端
測試環境:
nginx: CentOS7 epel 安裝nginx WAN:172.16.3.152 LAN:192.168.10.254
tomcat A: CentOS7 node1.san.com epel 安裝 tomcat 7 openjdk-1.8.0 memcached(現實環境中單獨服務器)
tomcat B: CentOS7 nodde2.san.com epel 安裝 tomcat 7 openjdk-1.8.0 memcached 現實環境中單獨服務器)
測試客戶端ubuntu 16.04 java
cat /etc/hosts 172.16.3.152 www.san.com
nginx安裝node
[root@nginx ~]# yum install epel-release -y [root@nginx ~]# yum install nginx -y
nginx配置
在/etc/nginx/nginx.conf http段添加以下行nginx
upstream tcsrvs { server 192.168.10.11:8080; server 192.168.10.12:8080; }
cat /etc/nginx/conf.d/san.com.confweb
[root@nginx ~]# cat /etc/nginx/conf.d/san.com.conf server { listen 80; server_name www.san.com; location / { proxy_pass http://tcsrvs; } }
Tomcat配置:
兩臺均須要安裝redis
#yum install epel-release -y #yum install java-1.8.0 java-1.8.0-openjdk-devel tomcat tomcat-webapps tomcat-admin-webapps tomcat-docs-webapp -y
說明:也能夠經過oracle官方下載jdk 下載tomcat 解壓到指定目錄並添加環境變量;通常企業推薦此種方式;爲了快捷,咱們用epel倉庫中的穩定版本;算法
添加測試頁
yum 安裝的tomcat工做目錄在/var/lib/tomcat/webapps 分別在node1與node2上,此目錄下建立測試項目,title 分別叫Tomcat A與Tomcat B 顏色分別爲green與red;以示區別;生產環境node1 與node2內容一致;這裏爲了測試區別node1與node2內容;apache
#mkdir -pv /var/lib/tomcat/webapps/test/{WEB-INF,META-INF,classes,lib} #cat /var/lib/tomcat/webapps/test/index.jsp <%@ page language="java" %> <html> <head><title>Tomcat A</title></head> <body> <h1><font color="red">TomcatA.san.com</font></h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("san.com","san.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
配置管理頁密碼
tomcat與管理程序安裝好後配置訪問密碼
修改註釋/etc/tomcat/tomcat-users.xml文件
<role rolename="admin-gui"/> <role rolename="manager-gui"/> <user username="tomcat" password="tomcat" roles="manager-gui,admin-gui"/>
備份默認/etc/tomcat/server.xml文件
cd /etc/tomcat cp server.xml server.xml_def
測試頁訪問
http://www.san.com/test 如圖:出現Tomcat A
Ctrl+F5強制刷新 又出現Tomcat B
引起問題:若是是兩臺內容同樣的配置,客戶端訪問刷新一下就換到另外一個後端處理;相似經過session保留信息的服務(購買物車)如何保留?換句話說,如何保持會話不中斷,不管請求被分配到那一個後端?
解決方案
1)會話sticky(粘性):分爲source_ip 基於源ip和cookie
source_ip在不一樣的調度器上有不一樣的實現方式:
lvs:sh算法;
nginx:ip_hash或hash $request_uri consistent(一致性哈希算法)
haproxy: source
cookie:
nginx:hash 或 hash $cookie_name consistent;
haproxy:cookie
2)會話集羣(session cluster):delta session manager
3)session server: redis(store),memcached(cache)
如下基於tomcat自帶會話集羣與memcached實現會話保持 功能;
Tomcat Session Replication Cluster中文又叫 tomcat 會話複製集羣,即會話經過組播方式複製到每個後端tomcat節點;
可參考自帶幫助文檔:http://www.san.com/docs/cluster-howto.html
兩臺node1 node2節點/etc/hosts中添加以下:
#cat /etc/hosts 192.168.10.11 node1.san.com node1 192.168.10.12 node2.san.com node2
兩臺tomcat 節點sever.xml的Host字段中添加以下內容:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <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.10.0.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" <!-- 若是沒有/etc/hosts解析則須要本機ip --> port="4000" autoBind="100" selectorTimeout="5000" 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"/> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> <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.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster>
複製 /etc/tomcat/web.xml /var/lib/tomcat/webapps/test/WEB-INF/ 下並在web.xml的"<web-app >"字段下添加 "<distributable/>";
重啓tomcat 並再次訪問http://www.san.com/test 如圖:
Ctrl + F5強制刷新如圖:
能夠可出會話獲得保持,只要是從同一個客戶端中請求,刷新或關閉從新打開(基於同一個瀏覽器) 只要會話沒有過時,會話(session id) 不管來自那個後端,均是同樣;
缺點:
tomcat自帶支持會話集羣(能過多播方式發送各節點);但有一個缺點;後端tomcat節點過多時效率低下,不適用大規模;
原理說明:
客戶端請求到達前端nginx調度器並分配到後端某tomcat節點時,tomcat會優先使用本機內存保存session,當一個請求結束後,tomcat會經過第三方組件(kryo,javolution,xstream,flexjson)把session序列化併發送到memcached節點上存放做備份,第二次請求時,若是本地有session就直接返回,第二次請求結束,把session修改後的信息更新到後端的memcached服務器,以這樣的方式來保持本地的session與memcached上的session同步。當這個tomcat節點宕機時,那麼用戶的下一次請求就會被前端的負載均衡器路由到另外一個tomcat節點上,而這個節點上並無這個用戶的session信息,這個節點就從memcached服務器上去讀取session,並把session保存到本地的內存,當請求結束,session又被修改,再送回到memcached進行存放備份
當後端配置了多臺memcached時,tomcat在更新session信息時會同時向多個memcached節點更新session,當一個memcached節點故障時,tomcat能夠從選擇一個正常工做的memcached節點讀取session信息來發送給用戶的瀏覽器,讓其重置session信息,這樣,memcached也達到了高可用的目的;
如下操做均在兩臺node上操做
還原默認配置文件
#cd /etc/tomcat/ #cp server.xml server.xml_cluster #cp server.xml_def server.xml #systemctl stop tomcat
安裝memcached服務
#yum install memcached -y #systemctl start memcached
memcache配置(默認便可,生產環境時須要加大內存與併發鏈接數)
# cat /etc/sysconfig/memcached PORT="11211" USER="memcached" MAXCONN="1024" CACHESIZE="64" OPTIONS=""
兩臺/etc/tomcat/server.xml Host段中添加以下內容:
<Context path="/test" docBase="test" reloadable="true"> <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="m1:192.168.10.11:11211,m2:192.168.10.12:11211" failoverNodes="m1" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"/> </Context>
說明:
添加兩個冗餘備份memcached節點分別叫m1,m2 failoverNodes="m1" 表示m1做爲備份;當m2失敗時鏈接;即便用m2;
安裝對應版本組件
下載如下JAR包到tomcat庫目錄; cd /usr/share/tomcat/lib wget http://www.java2s.com/Code/JarDownload/javolution/javolution-5.5.1.jar.zip #須要解壓 unzip javolution-5.5.1.jar.zip wget http://repo1.maven.org/maven2/net/spy/spymemcached/2.12.1/spymemcached-2.12.1.jar wget http://repo1.maven.org/maven2/de/javakaffee/msm/msm-javolution-serializer/2.1.1/msm-javolution-serializer-2.1.1.jar wget http://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager-tc7/2.1.1/memcached-session-manager-tc7-2.1.1.jar wget http://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager/2.1.1/memcached-session-manager-2.1.1.jar
注意:epel安裝的tomcat 和openjdk版本以下:
openjdk: "1.8.0_161"
tomcat : "7.0.76"
以上第三方插件須和對應的版本是兼容的;如發現tomcat啓動有問題;沒法訪問或以下相似錯誤
#tail -fn 100 /var/log/tomcat/catalina.xxxx.log 三月 23, 2018 4:12:52 下午 org.apache.catalina.core.StandardContext startInternal 嚴重: The session manager failed to start org.apache.catalina.LifecycleException: Failed to start component [de.javakaffee.web.msm.MemcachedBackupSessionManager[/test]] at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:162) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5643) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
則表示第三方組件與tomcat不兼容!請從新下載版本;
測試:
瀏覽器訪問http://www.san.com/test 如圖:
Ctrl+F5強刷新 如圖:
從測試上能夠看出目前已經經過memcache存儲session等緩存信息;並同步到兩臺memcache上;當前只使用m2節點;
經過nginx快速實現負載tomcat應用;引用session不可保持問題;經過自帶的Tomcat Session Replication Cluster和結合memcached及第三方組件實現高可用會話緩存服務來保持會話;前者不適合大規模應用;