一、概述
之前文章已經描述了企業高可用負載相關的架構及實現,其中常用的nginx或haproxy,LVS結合keepalived做前端高可用調度器;但之前沒有提到會話高可用保持;
本文通過 Tomcat Session Replication Cluster(tomcat自帶)和tomcat結合memcat及第三方組件實現Tomcat Memcache Session Server高可用會話緩存服務;
實現的效果:
同一客戶端訪問業務網站,經過調度器負載調度到達後端,不管選擇的是那個後端,session ID都不變,都保存在兩臺或多臺的memcached緩存中(負載冗餘);以保持持會話;
架構圖:
說明:客戶端請求時nginx通過負載調度算法將請求調度至某一後端服務器;tomcat 把會話通過組播的方式複製到集羣各節點;所有節點共享會話;
說明:客戶端請求時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
cat /etc/hosts
172.16.3.152 www.san.com
二、安裝配置集羣
nginx安裝
[root@nginx ~]# yum install epel-release -y
[root@nginx ~]# yum install nginx -y
nginx配置
在/etc/nginx/nginx.conf http段添加如下行
upstream tcsrvs {
server 192.168.10.11:8080;
server 192.168.10.12:8080;
}
cat /etc/nginx/conf.d/san.com.conf
[root@nginx ~]# cat /etc/nginx/conf.d/san.com.conf
server {
listen 80;
server_name www.san.com;
location / {
proxy_pass http://tcsrvs;
}
}
Tomcat配置:
兩臺均需要安裝
#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內容;
#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 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節點過多時效率低下,不適用大規模;
四、Tomcat Memcache Session Server高可用配置
原理說明:
客戶端請求到達前端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及第三方組件實現高可用會話緩存服務來保持會話;前者不適合大規模應用;