目錄
1、測試環境概述
2、MSM簡介
2.1、MSM的特性
2.2、MSM要解決的問題
2.3、MSM的工作原理
3、環境搭建
3.1、memcached安裝
3.2、jdk與tomcat安裝配置
3.3、MSM sticky session + kryo模式的配置
3.4、MSM no-sticky session + kryo模式的配置
4、思考與總結
1、測試環境概述
採用兩臺linux x64主機,主機上分別安裝memcached與tomcat,memcached提供key/value的存儲服務,tomcat提供jsp程序的web容器,兩主機關閉iptables,關閉selinux。
主機規劃如下:
主機1
IP地址:192.168.0.201
主機名:nod2.tes.com 別名:nod2
安裝服務:jdk、tomcat、memcached
主機2
IP地址:192.168.0.202
主機名:nod3.tes.com 別名:nod3
安裝服務:jdk、tomcat、memcached
簡易拓撲如下:
<tomcat1> <tomcat2>
. \ / .
. X .
. / \ .
<memcached1> <memcached2>
2、MSM簡介
MSM全稱爲Memcached Session Manager(Memcached會話管理器),是tomcat的用戶session信息存放在像memcached這樣的兼容key/value存儲裏的高可用解決方案,這裏常使用的key/value服務有memcached與membase兩種。
2.1、MSM的特性MSM的特性
a、支持tomcat 6,tomcat 7,tomcat 8
b、支持sticky session或no-sticky session
c、無單點故障
d、tomcat故障轉移
e、memcached故障轉移
f、附帶串行化插件
g、支持異步session存儲,擁有更快的性能
....
2.2、MSM要解決的問題
假如有一個web app運行在一個tomcat集羣中,前端通過apache的mod_jk或mod_proxy實現tomcat的負載均衡集羣,你想實現用戶session的故障轉移,從而達到用戶session信息的不丟失。在前邊的博文中已提出了一個“集羣/session複製”的解決方案(博文地址:http://zhaochj.blog.51cto.com/368705/1650728),此種方案有一個缺點,當tomcat集羣節點多餘4或5個時,集羣的性能就可能達到一個瓶頸,因其內部的session複製的實現是通過組播實現,對網絡的壓力很大,官方建議此種方案只適合小規格集羣的環境中。而MSM能真正的解決這個問題,是session會話共享的一個可伸縮性解決方案,MSM是把用戶的session信息存放在memcached中,假如一個tomcat節點死掉,其他的tomcat節點將接管工作並從後端的memcached服務器中取得之前的session信息,這保證了用戶會話不會丟失,而後端的memcached的節點可以不只一個,配置多個memcached節點又保證了memcached的單點故障。
2.3、MSM的工作原理
MSM支持兩種工作模式,sticky session和no-sticky session(從memcached-session-manager-1.4.0開始支持no-sticky session)。
sticky session模式:
安裝了MSM的tomcat會優先使用本機內存保存session,當一個請求結束後,MSM會把session發送到memcached節點上存放以作備份,第二次請求時,如果本地有session就直接返回,第二次請求結束,把session修改後的信息更新到後端的memcached服務器,以這樣的方式來保持本地的session與memcached上的session同步。當這個tomcat節點宕機時,那麼用戶的下一次請求就會被前端的負載均衡器路由到另一個tomcat節點上,而這個節點上並沒有這個用戶的session信息,這個節點就從memcached服務器上去讀取session,並把session保存到本地的內存,當請求結束,session又被修改,再送回到memcached進行存放備份。結合下邊的圖就更能理解MSM基於sticky session的工作原理。
注:圖片來自網絡
另外,當後端配置了多臺memcached時,MSM在更新session信息時會同時向多個memcached節點更新session,當一個memcached節點故障時,tomcat可以從選擇一個正常工作的memcached節點讀取session信息來發送給用戶的瀏覽器,讓其重置session信息,這樣,memcached也達到了高可用的目的。
no-sticky session模式:
假設後端配置了兩個memcached服務器,memcached1和memcached2,在這種配置的配置方法中沒有發現有能設置哪個memcached是主,哪個是備,所以我猜想是MSM自身來確定哪個是主,哪個是備,爲了描述no-sticky sessio的工作過程,假設memcahced1是主,memcached2是備。當請求到來時,MSM從memcached2(備)上讀取session信息,如果沒有就從memcached1(主)上讀取,如果有那就讀取到本地,如果沒有那就在本地創建session,當請求結束時,把本地的session信息寫回到memcached1和memcached2,並且要清除本地的session。結合下邊的圖,更能理解此模式的工作原理:
注:圖片來自網絡
3、環境搭建
3.1、memcached安裝
在yum源中的memcached的版本也比較新,所以直接採用yum進行安裝,如下:
[root@nod2 ~]# yum -y install memcached
[root@nod2 ~]# service memcached start
在nod3上依然採取yum安裝,略。
3.2、jkd與tomcat安裝配置
[root@nod2 msm]# pwd /root/software/msm [root@nod2 msm]# ls apache-tomcat-7.0.62.tar.gz jdk-8u45-linux-x64.rpm msm_kryo_serializers [root@nod2 msm]# ls msm_kryo_serializers/ asm-3.2.jar memcached-session-manager-1.8.3.jar msm-kryo-serializer-1.8.3.jar kryo-1.04.jar memcached-session-manager-tc7-1.8.3.jar reflectasm-1.01.jar kryo-serializers-0.11.jar minlog-1.2.jar spymemcached-2.11.1.jar
上邊的這些jar包分爲兩類,一類是關於msm的包:
memcached-session-manager-1.8.3.jar
memcached-session-manager-tc7-1.8.3.jar
另一類是kryo序列化的jar包:
asm-3.2.jar msm-kryo-serializer-1.8.3.jar kryo-serializers-0.11.jar minlog-1.2.jar spymemcached-2.11.1.jar kryo-1.04.jar reflectasm-1.01.jar
這些包的下載地址在這裏http://code.google.com/p/memcached-session-manager/wiki/SetupAndConfiguration
[root@nod2 msm]# rpm -ivh jdk-8u45-linux-x64.rpm [root@nod2 msm]# /usr/java/latest/bin/java -version java version "1.8.0_45" Java(TM) SE Runtime Environment (build 1.8.0_45-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode) [root@nod2 msm]# vim /etc/profile.d/java.sh JAVA_HOME=/usr/java/latest PATH=$JAVA_HOME/bin:$PATH export JAVA_HOME PATH [root@nod2 msm]# source /etc/profile.d/java.sh [root@nod2 msm]# tar xf apache-tomcat-7.0.62.tar.gz -C /usr/local/ [root@nod2 msm]# cd /usr/local/ [root@nod2 local]# ln -sv apache-tomcat-7.0.62 tomcat [root@nod2 local]# /usr/local/tomcat/bin/catalina.sh version Using CATALINA_BASE: /usr/local/tomcat Using CATALINA_HOME: /usr/local/tomcat Using CATALINA_TMPDIR: /usr/local/tomcat/temp Using JRE_HOME: /usr/java/latest Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar Server version: Apache Tomcat/7.0.62 Server built: May 7 2015 17:14:55 UTC Server number: 7.0.62.0 OS Name: Linux OS Version: 2.6.32-358.el6.x86_64 Architecture: amd64 JVM Version: 1.8.0_45-b14 JVM Vendor: Oracle Corporation [root@nod2 local]# vim /etc/profile.d/tomcat.sh CATALINA_HOME=/usr/local/tomcat PATH=$CATALINA_HOME/bin:$PATH export CATALINA_HOME PATH [root@nod2 local]# source /etc/profile.d/tomcat.sh
爲tomcat提供一個啓用腳本:
[root@nod2 ~]# vim /etc/rc.d/init.d/tomcat #!/bin/sh #Description: This shell script manage apache tomcat. #Author: zhaochj #Time: 2015-5-18 #Version: 1.0 case $1 in "start") /usr/local/tomcat/bin/catalina.sh start ;; "stop") /usr/local/tomcat/bin/catalina.sh stop ;; "restart") /usr/local/tomcat/bin/catalina.sh stop sleep 3 /usr/local/tomcat/bin/catalina.sh start ;; *) echo "Usage:`basename $0` {start|stop|restart}" exit 1 ;; esac [root@nod2 ~]# chmod +x /etc/rc.d/init.d/tomcat
在nod3上以同樣的方法安裝jdk與tomcat。
兩個節點都啓動tomcat,測試一下能否進入默認界面,經測試,兩個節點都能進入tomcat的默認界面,但在catalina.out日誌輸出中發現有如下提示:
[root@nod2 tomcat]# tail /usr/local/tomcat/logs/catalina.out ...... INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib ....
這是因爲tomcat可以整合本地的apr,使用tomcat處理靜態資源時性能更好,要想啓用tomcat的此功能,進行如下配置:
[root@nod2 bin]# pwd /usr/local/tomcat/bin [root@nod2 bin]# tar xf tomcat-native.tar.gz [root@nod2 bin]# cd tomcat-native-1.1.33-src/jni/native/ [root@nod2 native]# ./configure --with-apr=/usr/bin/apr-1-config --with-java-home=/usr/java/jdk1.8.0_45 [root@nod2 native]# make && make install [root@nod2 native]# ls /usr/local/apr/lib/ libtcnative-1.a libtcnative-1.la libtcnative-1.so libtcnative-1.so.0 libtcnative-1.so.0.1.33 pkgconfig [root@nod2 native]# cp /usr/local/apr/lib/libtcnative-1.so /usr/lib64/
重啓tomcat,再觀察輸出日誌中有
INFO: Loaded APR based Apache Tomcat Native library 1.1.33 using APR version 1.3.9.
May 18, 2015 5:03:00 PM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true]
則證明apr的問題已解決。
接着創建虛擬主機及增加jvmRoute:
[root@nod2 tomcat]# vim /usr/local/tomcat/conf/server.xml 把<Engine name="Catalina" defaultHost="localhost">修改成<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1"> 在“nod3”上把<Engine name="Catalina" defaultHost="localhost">修改成<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2"> 在<Engine>容器中增加一個<Host>容器,內容如下: <Engine> ..... <Host name="nod2.test.com" appBase="/tomcat/app" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="mysite"/> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="nod2_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> ...... </Engine>
在"nod3"上增加以下內容:
<Engine> ..... <Host name="nod3.test.com" appBase="/tomcat/app" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="mysite"/> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="nod3_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> ...... </Engine>
接下來爲tomcat提供一個工程(nod2與nod3上創建過程一樣):
#創建一個工程目錄mysite [root@nod2 ~]# mkdir -pv /tomcat/app/mysite [root@nod2 ~]# cp -r /usr/local/tomcat/webapps/host-manager/WEB-INF/ /tomcat/app/mysite [root@nod2 ~]# vim /tomcat/app/mysite/index.jsp #一個測試所用的工程,nod3上把此文件中的“nod2”修改爲“nod3” <%@ page language="java" %> <html> <head><title>nod2</title></head> <body> <h1><font color="red">nod2</h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("abc","abc"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html> 兩個節點都配置好後,啓動tomcat,測試: [root@nod2 ~]# service tomcat start [root@nod3 ~]# service tomcat start
3.3、MSM sticky session + kryo模式的配置
在進行配置之前,把MSM和序列化需要的包準備好。
[root@nod2 msm]# pwd /root/software/msm [root@nod2 msm]# ls msm_kryo_serializers/ asm-3.2.jar memcached-session-manager-1.8.3.jar msm-kryo-serializer-1.8.3.jar kryo-1.04.jar memcached-session-manager-tc7-1.8.3.jar reflectasm-1.01.jar kryo-serializers-0.11.jar minlog-1.2.jar spymemcached-2.11.1.jar
把上邊的所有jar包放在$CATALINA_HOME/lib目錄內:
[root@nod2 msm]# cp msm_kryo_serializers/* /usr/local/tomcat/lib/
接着更新<Context>元素,建議最好不要在$CATALINA_HOME/conf/server.xml定義,這樣太具有侵略性,對整個tomcat都有效。應配置$CATALINA_HOME/conf/context.xml文件。
在配置tomcat相關的配置文件時,應該先停止tomcat服務,再做修改。
把以下代碼加入到context.xml中,兩個節點都要進行修改:
<Context> ..... <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="memcached1:192.168.0.201:11211,memcached2:192.168.0.202:11211" failoverNodes="memcached1" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"/> ..... </Context>
failoverNodes="memcached1"告訴MSM把session信息優先存放在memcached2中,只有當memcached2不可用時,才把session存放在memcached1中,memcached1是一個失效轉移的節點。
在nod2上設置failoverNodes="memcached1",在nod3上設置failoverNodes="memcached2",這不是必須,我只是用來驗證兩個節點到底是把session優先存放在哪個memcached上。
文件修改好後,啓動tomcat服務,分別訪問兩個節點進行測試:
看上邊兩個節點的seesion的返回信息,nod2把session存放在了memcached2上,此節點上的failoverNodes="memcached1";nod3上剛是把session存放在memcached1上,它的failoverNodes="memcached2"。
現在我把nod2上的memcached關閉,再來看下兩個節點的返回信息。
[root@nod2 webapps]# service memcached stop Stopping memcached: [ OK ]
nod3把session的存放節點重定向到了memcached2上了,我再啓動nod2上的memcached服務,nod3頁面不會自動再定向回memcached1,我再關閉nod2上的memcached服務,訪問我們的兩個站點,Session ID中的memcached1或memcached2都能正常的切換,切換後前邊的session id也是保持不變的,說明用戶的session信息真正的保存在了memcached服務器上。
3.4、MSM no-sticky session + kryo模式的配置
基於no-sticky seesion + kryo的模式,只需要把要增加在context.xml的代碼更換成下邊的即可
<Context> .... <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="memcached1:192.168.0.201:11211,memcached2:192.168.0.202:11211" sticky="false" sessionBackupAsync="false" lockingMode="uriPattern:/path1|/path2" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"/> ...... </Context>
經測試,在no-sticky session模式下,後端的memcached各節點沒有主、備之分,完全由MSM自行決定使用哪個memecached節點,用戶的session信息由MSM寫入到各個memcached中,只要有一個memcached節點正常工作,那用戶的session信息就不會丟失。
4、思考與總結
至此,此博文介紹完了利用MSM來構建一套可伸縮的高可用session共享集羣,此博文是參考了官方文檔和網絡上的資料,再加上不斷的摸索搭建出來的,在做環境測試時有個細節值得注意,起初在啓用、停止tomcat服務時,我沒有在意日誌的輸出,只是查看監聽的端口是否在監聽“8080”端口,而且訪問也是正常的,但當我不經意去查看日誌輸出時,發現日誌給予我們好多細節,且還有些錯誤,當一些錯誤發生時,tomcat還能正常的訪問,如果是在生產環境下會有隱患的,所以在搭建環境,調度程序時,在程序的啓動、停止時都要不斷的監視日誌輸出信息,確保環境搭建好後,日誌中不會有錯誤信息,如果有警告也要仔細分析,此信息的產生由來,是否會帶來隱患等。
這次環境搭建好後,在關閉tomcat時,日誌輸出中還有以下的信息:
SEVERE: The web application [/docs] appears to have started a thread named [Memcached IO over {MemcachedConnection to /192.168.0.202:11211 /192.168.0.201:11211} - SHUTTING DOWN (informed client)] but has failed to stop it. This is very likely to create a memory leak.
通過google也沒有得到什麼解決方案,只是說這個警告信息不是什麼應用程序所報的信息,但最好還是不要有。(此問題的相關信息在這裏https://code.google.com/p/memcached-session-manager/issues/detail?id=197)