MSM實現tomcat集羣中session共享的高可用

目錄

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的工作原理。

wKiom1VZ8xPTBIllAAF63FF9JOc518.jpg

注:圖片來自網絡

另外,當後端配置了多臺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。結合下邊的圖,更能理解此模式的工作原理:

wKioL1VZ9LLT-G5nAAFyEE1HhJo565.jpg

注:圖片來自網絡

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 &quot;%r&quot; %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 &quot;%r&quot; %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

wKiom1VZ82bApdh4AADJeQb--Ak907.jpg

wKiom1VZ83mxFoD5AADD7p8vkX8660.jpg

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服務,分別訪問兩個節點進行測試:

wKioL1VZ9RfTaX-9AADa8I0KZes153.jpg

wKioL1VZ9S6TVJqvAAC09L9HFaY118.jpg

看上邊兩個節點的seesion的返回信息,nod2把session存放在了memcached2上,此節點上的failoverNodes="memcached1";nod3上剛是把session存放在memcached1上,它的failoverNodes="memcached2"。

現在我把nod2上的memcached關閉,再來看下兩個節點的返回信息。

[root@nod2 webapps]# service memcached stop
Stopping memcached:                                        [  OK  ]

wKioL1VZ9VaRVCkxAADZcYdDsPM084.jpg

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章