Tomcat經過Memcached實現session共享的完整部署記錄

 

對於web應用集羣的技術實現而言,最大的難點就是:如何能在集羣中的多個節點之間保持數據的一致性,會話(Session)信息是這些數據中最重要的一塊。要實現這一點, 大致上有兩種方式:
一種是把全部Session數據放到一臺服務器上或者數據庫中,集羣中的全部節點經過訪問這臺Session服務器來獲取數據;
另外一種就是在集羣中的全部節點間進行Session數據的同步拷貝,任何一個節點均保存了全部的Session數據。javascript

在集羣系統下實現session統一的有以下幾種方案:
1) 請求精肯定位:session sticky,例如基於訪問ip的hash策略,即當前用戶的請求都集中定位到一臺服務器中,這樣單臺服務器保存了用戶的session登陸信息,若是宕機,則等同於單點部署,會丟失,會話不復制。
2) session複製共享:session replication,如tomcat自帶session共享,主要是指集羣環境下,多臺應用服務器之間同步session,使session保持一致,對外透明。 若是其中一臺服務器發生故障,根據負載均衡的原理,調度器會遍歷尋找可用節點,分發請求,因爲session已同步,故能保證用戶的session信息不會丟失,會話複製,。
此方案的不足之處:
必須在同一種中間件之間完成(如:tomcat-tomcat之間).
session複製帶來的性能損失會快速增長.特別是當session中保存了較大的對象,並且對象變化較快時, 性能降低更加顯著,會消耗系統性能。這種特性使得web應用的水平擴展受到了限制。
Session內容經過廣播同步給成員,會形成網絡流量瓶頸,即使是內網瓶頸。
在大併發下表現並很差
3) 基於cache DB緩存的session共享
基於memcache/redis緩存的session共享.即便用cacheDB存取session信息,應用服務器接受新請求將session信息保存在cache DB中,當應用服務器發生故障時,調度器會遍歷尋找可用節點,分發請求,當應用服務器發現session不在本機內存時,則去cacheDB中查找,若是找到則複製到本機,這樣實現session共享和高可用。php

Tomcat集羣session同步方案有如下幾種方式:
1)使用tomcat自帶的cluster方式,多個tomcat間自動實時複製session信息,配置起來很簡單。但這個方案的效率比較低,在大併發下表現並很差。
2)利用nginx的基於訪問ip的hash路由策略,保證訪問的ip始終被路由到同一個tomcat上,這個配置更簡單。每一個請求按訪問ip的hash結果分配,這樣每一個訪客固定訪問一個後端服務器,能夠解決session(並非共享session解決)的問題! 而且若是應用是某一個局域網大量用戶同時登陸,這樣負載均衡就沒什麼做用了。
3)利用nginx插件實現tomcat集羣和session同步,nginx-upstream-jvm-route是一個Nginx的擴展模塊,用來實現基於Cookie的Session Sticky的功能。可是遺憾的是,這個模塊的補丁在nginx1.4版本以後就沒有再更新了,因此nginx1.4以後版本跟該模塊就不兼容了!!  
4)利用memcached實現(MSM工具)。memcached存儲session,並把多個tomcat的session集中管理,前端在利用nginx負載均衡和動靜態資源分離,在兼顧系統水平擴展的同時又能保證較高的性能。即經過MSM工具把Tomcat的Session序列化後保存到Memcached裏面,從而實現Session共享.
5)利用redis實現。使用redis不只僅能夠將緩存的session持久化,還由於它支持的單個對象比較大,並且數據類型豐富,不僅是緩存 session,還能夠作其餘用途,能夠一舉幾得。Redis這種方式目前還不支持Tomcat8環境(如今網上插件不支持tomcat8,非要支持tomcat8,則需修改插件jar包的源代碼)!
6)利用filter方法實現。這種方法比較推薦,由於它的服務器使用範圍比較多,不只限於tomcat ,並且實現的原理比較簡單容易控制。
7)利用terracotta服務器共享session。這種方式配置比較複雜。css

在Tomcat集羣中,當一個節點出現故障,雖然有高可用集羣來負責故障轉移,但用戶的session信息如何保持呢?
下面介紹第4種方案,session複製同步使用MSM(Memcache-Session-Manager),即利用MSM+Memcached作Session共享。html

MSM介紹:(詳細介紹能夠參考:http://www.cnblogs.com/kevingrace/p/6401025.html
MSM是一個高可用的Tomcat Session共享解決方案,除了能夠從本機內存快速讀取Session信息(僅針對黏性Session)外,還可以使用Memcached存取Session,以實現高可用。
傳統tomcat集羣,會話複製隨着結點數增多,擴展性成爲瓶頸。MSM使用memcached完成統一管理tomcat會話,避免tomcat結點間過多會話複製。
MSM利用Value(Tomcat 閥)對Request進行跟蹤。Request請求到來時,從memcached加載session,Request請求結束時,將tomcat session更新至memcached,以達到session共享之目的, 支持sticky和non-sticky模式:
sticky : 會話粘連模式(黏性session)。客戶端在一臺tomcat實例上完成登陸後,之後的請求均會根據IP直接綁定到該tomcat實例。
no-sticky:會話非粘連模式(非粘性session)。客戶端的請求是隨機分發,多臺tomcat實例均會收到請求。前端

在進行環境部署以前,要對cookie和session的工做機制很是瞭解,若是不瞭解其中的原理且只是機械性地去按照參考文檔部署,那麼這是毫無心義的。
a)cookie是怎麼工做的?
加入咱們建立了一個名字爲login的Cookie來包含訪問者的信息,建立Cookie時,服務器端的Header以下面所示,這裏假設訪問者的註冊名是「wangshibo」,同時還對所建立的Cookie的屬性如path、domain、expires等進行了指定。java

Set-Cookie:login=wangshibo;path=/;domain=msn.com; 
expires=Monday,01-Mar-99 00:00:01 GMT 

上面這個Header會自動在瀏覽器端計算機的Cookie文件中添加一條記錄。瀏覽器將變量名爲「login」的Cookie賦值爲「wangshibo」。
注意,在實際傳遞過程當中這個Cookie的值是通過了URLEncode方法的URL編碼操做的。 這個含有Cookie值的HTTP Header被保存到瀏覽器的Cookie文件後,Header就通知瀏覽器將Cookie經過請求以忽略路徑的方式返回到服務器,完成瀏覽器的認證操做。
此外,咱們使用了Cookie的一些屬性來限定該Cookie的使用。例如Domain屬性可以在瀏覽器端對Cookie發送進行限定,具體到上面的例子,該Cookie只能傳到指定的服務器上,而決不會跑到其餘的Web站點上去。Expires屬性則指定了該Cookie保存的時間期限,例如上面的Cookie在瀏覽器上只保存到1999年3月1日1秒。 固然,若是瀏覽器上Cookie太多,超過了系統所容許的範圍,瀏覽器將自動對它進行刪除。至於屬性Path,用來指定Cookie將被髮送到服務器的哪個目錄路徑下。
說明:瀏覽器建立了一個Cookie後,對於每個針對該網站的請求,都會在Header中帶着這個Cookie;不過,對於其餘網站的請求Cookie是絕對不會跟着發送的。並且瀏覽器會這樣一直髮送,直到Cookie過時爲止。node

b)session是如何工做的?
因爲http是無狀態的協議,你訪問了頁面A,而後再訪問B頁面,http沒法肯定這2個訪問來自一我的,所以要用cookie或session來跟蹤用戶,根據受權和用戶身份來 顯示不一樣的頁面。好比用戶A登錄了,那麼能看到本身的我的信息,而B沒登錄,沒法看到我的信息。還有A可能在購物,把商品放入購物車,此時B也有這個過程, 你沒法肯定A,B的身份和購物信息,因此須要一個session ID來維持這個過程。
cookie是服務器發給客戶端並保持在客戶端的一個文件,裏面包含了用戶的訪問信息(帳戶密碼等),能夠手動刪除或設置有效期,在下次訪問的時候,會返給服務器。
注意:cookie能夠被禁用,因此要想其餘辦法,這就是session。cookie數據存放在客戶的瀏覽器上,session數據放在服務器上。cookie同時也是session id的載體,cookie保存session id。另外:cookie不是很安全,別人能夠分析存放在本地的cookie並進行cookie欺騙,考慮到安全應當使用session。session是服務器端緩存,cookie是客戶端緩存。因此建議:將登錄信息等重要信息存放爲session;其餘信息若是須要保留,能夠放在cookie中,
好比:你去商場購物,商場會給你辦一張會員卡,下次你來出示該卡,會有打折優惠,該卡能夠本身保存(cookie),或是商場代爲保管,因爲會員太多,我的須要保存卡號信息(session ID)。 linux

爲何要持久化session(共享session)?
由於:在客戶端每一個用戶的Session對象存在Servlet容器中,若是Tomcat服務器重啓或者宕機的話,那麼該session就會丟失,而客戶端的操做會因爲session丟失而形成數據丟失;若是當前用戶訪問量巨大,每一個用戶的Session裏存放大量數據的話,那麼就很佔用服務器大量的內存,進而導致服務器性能受到影響。
可使用數據庫持久化session,分爲物理數據庫和內存數據庫。物理數據庫備份session,因爲其性能緣由,不推薦;內存數據庫可使用redis和memcached,這裏介紹memcached的方法。nginx

MSM工做原理:
a)Sticky Session(黏性) 模式下的工做原理:
Tomcat本地Session爲主Session,Memcached 中的Session爲備Session。Request請求到來時, 從memcached加載備 Session到 tomcat (僅當tomcat jvmroute發生變化時, 不然直接取Tomcat Session);Request請求結束時,將Tomcat Session更新至memcached,以達到主備同步之目的。 安裝在Tomcat上的MSM使用本機內存保存Session,當一個請求執行完畢以後,若是對應的Session在本地不存在(即某用戶的第一次請求),則將該Session複製一份至Memcached;當該Session的下一個請求到達時,會使用Tomcat的本地Session,請求處理結束以後,Session的變化會同步更新到 Memcached,保證數據一致。當集羣中的一個Tomcat掛掉,下一次請求會被路由到其餘Tomcat上。負責處理此此請求的Tomcat並不清楚Session信息,因而從Memcached查找該Session,更新該Session並將其保存至本機。這次請求結束,Session被修改,送回Memcached備份。c++

b)Non-sticky Session (非黏性)模式下的工做原理(記住:多臺tomcat集羣或多個tomcat實例時須要選擇Non-Sticky模式,即sticky="false")
Tomcat本地Session爲中轉Session,Memcached1爲主Session,Memcached2爲備Session。Request請求到來時,從Memcached2加載備Session到tomcat,(當容器中仍是沒有Session 則從Memcached1加載主Session到tomcat,這種狀況是隻有一個memcached節點,或者有Memcached1 出錯時),Request請求結束時,將Tomcat Session更新至主Memcached1和備memcached2,而且清除Tomcat Session 。以達到主備同步之目的。 多臺tomcat集羣時 須要選擇Non-Sticky模式,即sticky="false"

===================================================
Tomcat8+Memcached+Nginx實現session會話共享的操做記錄

1)基礎環境

ip                 主機名           應用                       端口
192.168.10.200     Nginx-node       nginx1.12.2                80
192.168.10.201     Tomcat-node1     java8.13一、tomcat8.0.53    8080
192.168.10.202     Tomcat-node2     java8.13一、tomcat8.0.53    8080
192.168.10.203     Mem-node1        memcached-1.4.34           11211
192.168.10.205     Mem-node2        memcached-1.4.34           11211

這裏的兩臺memcache,基於tomcat作會話同步;(只對動態內容緩存,用於追蹤用戶會話)

使用Nginx+Tomcat進行負載均衡時,通常使用輪詢方式進行負載。可是若是使用輪詢方式的話,可能會訪問不一樣的Tomcat,
此時若是不進行Session共享,則至關因而一個新的Session。就好比現有系統都是須要認證登陸的系統,若是沒有Session共享,則會致使用戶退出登陸。
  
Nginx配置中可使用ip_hash的方式簡單實現session共享.可是這種方式只能將session固定到單臺tomcat機器上,若是這臺tomcat機器掛掉,則session
信息就會丟失,不能實現session的故障轉移.

下面操做在五臺機器上一樣執行:
[root@Nginx-node ~]# cat /etc/redhat-release
CentOS release 6.9 (Final)
   
爲了方便測試,關閉iptables防火牆和selinux。若是是生產環境,開啓iptables後,須要開放對應的應用端口。
[root@Nginx-node ~]# setenforce 0
[root@Nginx-node ~]# getenforce
disabled
[root@Nginx-node ~]# cat /etc/sysconfig/selinux |grep "SELINUX=disabled"
SELINUX=disabled
[root@Nginx-node ~]# /etc/init.d/iptables stop
   
本案例環境部署中所需的軟件下載地址:https://pan.baidu.com/s/1E92JgSov5IqHsY9wAzgRMA
提取密碼:hwpw
下載到服務器上的/usr/local/src目錄下.另外:節點服務器的系統時間必定要保持一致!!
 
memcached-session-manager環境部署所需的各類jar包的下載地址:
https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration

舒適提示:
Tomcat+MSM環境的部署對版本要求極其謹慎,本案例中下載的軟件版本是通過驗證後的正確版本,不可隨意更改這些軟件的版本,不然會致使環境部署失敗!

實驗拓撲圖:

2)安裝tomcat(在192.168.10.201和192.168.10.202兩臺機器上操做)

安裝java8環境。先卸載掉系統自帶的java7,而後安裝java8
[root@Tomcat-node1 ~]# java -version
java version "1.7.0_131"
OpenJDK Runtime Environment (rhel-2.6.9.0.el6_8-x86_64 u131-b00)
OpenJDK 64-Bit Server VM (build 24.131-b00, mixed mode)
[root@Tomcat-node1 ~]# yum -y remove java-1.7.0-openjdk*
[root@Tomcat-node1 ~]# yum -y remove tzdata-java.noarch
[root@Tomcat-node1 ~]# java -version                  
-bash: /usr/bin/java: No such file or directory
  
[root@Tomcat-node1 ~]# ll /usr/local/src/jdk-8u131-linux-x64_.rpm
-rw-rw-r--. 1 root root 169983496 Nov 19  2017 /usr/local/src/jdk-8u131-linux-x64_.rpm
[root@Tomcat-node1 ~]# rpm -ivh /usr/local/src/jdk-8u131-linux-x64_.rpm --force
[root@Tomcat-node1 ~]# vim /etc/profile
......
JAVA_HOME=/usr/java/jdk1.8.0_131
JAVA_BIN=/usr/java/jdk1.8.0_131/bin
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/bin:/sbin/
CLASSPATH=.:/lib/dt.jar:/lib/tools.jar
export JAVA_HOME JAVA_BIN PATH CLASSPATH
  
[root@Tomcat-node1 ~]# source /etc/profile
[root@Tomcat-node1 ~]# java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
You have new mail in /var/spool/mail/root
  
安裝配置tomcat8
[root@Tomcat-node1 ~]# cd /usr/local/src/
[root@Tomcat-node1 src]# ll apache-tomcat-8.0.53.tar.gz
-rw-rw-r--. 1 root root 9472492 Nov  9  2017 apache-tomcat-8.0.53.tar.gz
[root@Tomcat-node1 src]# tar -zvxf apache-tomcat-8.0.53.tar.gz
[root@Tomcat-node1 src]# mv apache-tomcat-8.0.53 /usr/local/tomcat8
  
啓動tomcat
[root@Tomcat-node1 src]# /usr/local/tomcat8/bin/startup.sh
Using CATALINA_BASE:   /usr/local/tomcat8
Using CATALINA_HOME:   /usr/local/tomcat8
Using CATALINA_TMPDIR: /usr/local/tomcat8/temp
Using JRE_HOME:        /usr/java/jdk1.8.0_131
Using CLASSPATH:       /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar
Tomcat started.
You have new mail in /var/spool/mail/root
[root@Tomcat-node1 src]# ps -ef|grep tomcat
root      8477     1 87 03:11 pts/0    00:00:03 /usr/java/jdk1.8.0_131/bin/java -Djava.util.logging.config.file=/usr/local/tomcat8/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -Djava.protocol.handler.pkgs=org.apache.catalina.webresources -classpath /usr/local/tomcat8/bin/bootstrap.jar:/usr/local/tomcat8/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat8 -Dcatalina.home=/usr/local/tomcat8 -Djava.io.tmpdir=/usr/local/tomcat8/temp org.apache.catalina.startup.Bootstrap start
root      8528  6829  0 03:11 pts/0    00:00:00 grep tomcat
[root@Tomcat-node1 src]# lsof -i:8080
COMMAND  PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
java    8477 root   49u  IPv6 12974768      0t0  TCP *:webcache (LISTEN)

再另外一臺tomcat節點192.168.10.202如上一樣部署。

編寫一個測試頁面(直接修改index.jsp文件):
[root@Tomcat-node1 ~]# cat /usr/local/tomcat8/webapps/ROOT/index.jsp 
<html>
    <body bgcolor="green">      
    <center>     
    <%=  request.getSession().getId()  %>     
    <h1>192.168.10.201</h1> 
    <h1>port:8080</h1>
    <h1>this is Tomcat-node1 ! </h1>  
    </center>
    </body>
</html>

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false"%>
SessionID:<%=session.getId()%><BR>
SessionIP:<%=request.getServerName()%> <BR>
SessionPort:<%=request.getServerPort()%>
<%     out.println("This is Tomcat server 201 !");     %>

另外一臺tomcat的測試頁面爲:
[root@Tomcat-node2 ~]# cat /usr/local/tomcat8/webapps/ROOT/index.jsp 
<html>
    <body bgcolor="green">      
    <center>     
    <%=  request.getSession().getId()  %>     
    <h1>192.168.10.202</h1> 
    <h1>port:8080</h1>
    <h1>this is Tomcat-node2! </h1>  
    </center>
    </body>
</html>

<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false"%>
SessionID:<%=session.getId()%><BR>
SessionIP:<%=request.getServerName()%> <BR>
SessionPort:<%=request.getServerPort()%>
<%     out.println("This is Tomcat server 202 !");     %>

3)安裝Nginx(在192.168.10.200機器上操做)

[root@Nginx-node ~]# cd /usr/local/src/
[root@Nginx-node src]# ll
total 8920
-rw-rw-r--. 1 root root  981687 Oct 27  2017 nginx-1.12.2.tar.gz
-rw-rw-r--. 1 root root 5453234 Aug 23  2018 openssl-1.1.0i.tar.gz
-rw-rw-r--. 1 root root 2081413 Aug 23  2018 pcre-8.42.tar.gz
-rw-rw-r--. 1 root root  607698 Jan 16  2017 zlib-1.2.11.tar.gz

安裝依賴包
[root@Nginx-node src]# yum -y install gcc gcc-c++

安裝pcre庫
[root@Nginx-node src]# tar -zvxf pcre-8.42.tar.gz
[root@Nginx-node src]# cd pcre-8.42
[root@Nginx-node pcre-8.42]# ./configure && make && make install

安裝zlib庫
[root@Nginx-node pcre-8.42]# cd /usr/local/src/
[root@Nginx-node src]# tar -zvxf zlib-1.2.11.tar.gz 
[root@Nginx-node src]# cd zlib-1.2.11
[root@Nginx-node zlib-1.2.11]# ./configure && make && make install

安裝openssl
[root@Nginx-node zlib-1.2.11]# cd /usr/local/src/
[root@Nginx-node src]# tar -zvxf openssl-1.1.0i.tar.gz 
[root@Nginx-node src]# cd openssl-1.1.0i
[root@Nginx-node openssl-1.1.0i]# ./config && make && make install

安裝nginx,特別注意要指定prce zlib openssl原碼包位置
[root@Nginx-node openssl-1.1.0i]# cd /usr/local/src/
[root@Nginx-node src]# tar -zvxf nginx-1.12.2.tar.gz 
[root@Nginx-node src]# cd nginx-1.12.2                 
[root@Nginx-node nginx-1.12.2]# ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-pcre=/usr/local/src/pcre-8.42 --with-zlib=/usr/local/src/zlib-1.2.11  --with-openssl=/usr/local/src/openssl-1.1.0i
[root@Nginx-node nginx-1.12.2]# make && make install

安裝成功後配置nginx
[root@Nginx-node nginx-1.12.2]# cd /usr/local/nginx/conf/
[root@Nginx-node conf]# cp nginx.conf nginx.conf.bak
[root@Nginx-node conf]# cat nginx.conf
#user  nobody;
worker_processes  8;
 
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
 
#pid        logs/nginx.pid;
 
worker_rlimit_nofile 65535;
events {
   use epoll;
    worker_connections  65535;
}
 
http {
    include       mime.types;
    default_type  application/octet-stream;
    charset utf-8;
         
    ######
    ## set access log format
    ######
    log_format  main  '$http_x_forwarded_for $remote_addr $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_cookie" $host $request_time';
 
    #######
    ## http setting
    #######
    sendfile       on;
    tcp_nopush     on;
    tcp_nodelay    on;
    keepalive_timeout  65;
    fastcgi_connect_timeout 30000;
    fastcgi_send_timeout 30000;
    fastcgi_read_timeout 30000;
    fastcgi_buffer_size 256k;
    fastcgi_buffers 8 256k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    fastcgi_intercept_errors on;
 
    ##cache##
    client_header_timeout 60s;
    client_body_timeout 60s;
    client_max_body_size 10m;
    client_body_buffer_size 1m;
    proxy_connect_timeout 5;
    proxy_read_timeout 60;
    proxy_send_timeout 5;              
    proxy_buffer_size 64k;
    proxy_buffers 4 128k;
    proxy_busy_buffers_size 128k;
    proxy_temp_file_write_size 1m;
    proxy_temp_path /home/temp_dir;
    proxy_cache_path /home/cache levels=1:2 keys_zone=cache_one:200m inactive=1d max_size=30g;
    ##end##
 
    gzip  on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 9;
    gzip_types       text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php;
    gzip_vary on;
 
    ## includes vhosts
    include vhosts/*.conf;
}

[root@Nginx-node conf]# mkdir vhosts
[root@Nginx-node conf]# cd vhosts/
[root@Nginx-node vhosts]# vim lb_tomcat.conf
  upstream tomcat-lb {
      server 192.168.10.201:8080;
      server 192.168.10.202:8080;
      }
                   
      server {
      listen  80;
      server_name www.kevin.com;
      location / {
          proxy_pass http://tomcat-lb;
          proxy_set_header  X-Real-IP $remote_addr;
          proxy_set_header  REMOTE-HOST $remote_addr;
          proxy_set_header  Host $host;
          proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
        }
        location ~ .*\.(gif|jpg|png|htm|html|css|ico|flv|swf)(.*) {
              proxy_pass http://tomcat-lb;
              proxy_redirect off;
              proxy_set_header Host $host;
              proxy_cache cache_one;
              proxy_cache_valid 200 302 1h;
              proxy_cache_valid 301 1d;
              proxy_cache_valid any 10m;
              expires 30d;
              proxy_cache_key $host$uri$is_args$args;
        }
}

[root@Nginx-node vhosts]# /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@Nginx-node conf]# /usr/local/nginx/sbin/nginx 
[root@Nginx-node conf]# lsof -i:80
COMMAND   PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
nginx   25292   root    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)
nginx   25293 nobody    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)
nginx   25294 nobody    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)
nginx   25295 nobody    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)
nginx   25296 nobody    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)
nginx   25297 nobody    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)
nginx   25298 nobody    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)
nginx   25299 nobody    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)
nginx   25300 nobody    6u  IPv4 19679665      0t0  TCP *:http (LISTEN)

將域名www.kevin.com解析到192.168.10.200上,訪問http://www.kevin.com,發現訪問請求結果會負載到192.168.10.201和192.168.10.202的tomcat上了。

 

如上,在配置memcached-session-manager會話共享以前,訪問http://www.kevin.com的請求會輪詢負載到tomcat-node1和tomcat-node2兩個節點上,而且session id會隨着頁面的刷新而改變,即此時尚未實現session會話共享!!

4)安裝Memcached(在192.168.10.203和192.168.10.205機器上操做)
Memcached是一款免費、開源、分佈式的內存對象緩存系統, 用於減小數據庫的負載, 加快web應用程序的訪問. Memcached簡單而且強大, 其簡單的設計加快了部署, 易於開發, 緩存解決了面臨的大量數據時不少的問題.

[root@mem-node1 ~]# yum -y install libevent libevent-devel
[root@mem-node1 ~]# cd /usr/local/src/
[root@mem-node1 src]# ll memcached-1.4.34.tar.gz                     
-rw-r--r-- 1 root root 391131 Jun 27 07:41 memcached-1.4.34.tar.gz
[root@mem-node1 src]# tar -zvxf memcached-1.4.34.tar.gz
[root@mem-node1 src]# cd memcached-1.4.34                 
[root@mem-node1 memcached-1.4.34]# ./configure --prefix=/usr/local/memcached
[root@mem-node1 memcached-1.4.34]# make && make install
  
啓動memcached,端口11211能夠根據本身須要修改不一樣端口
[root@mem-node1 ~]# /usr/local/memcached/bin/memcached -d -m 512 -u root -p 11211 -c 1024 -P /var/lib/memcached.11211pid
  
查看memcached進程是否起來
[root@mem-node1 ~]# ps -ef|grep memcached
root      1340     1  0 14:34 ?        00:00:00 /usr/local/memcached/bin/memcached -d -m 512 -u root -p 11211 -c 1024 -P /var/lib/memcached.11211pid
root      1400 16303  0 14:35 pts/0    00:00:00 grep memcached
[root@mem-node1 ~]# lsof -i:11211
COMMAND    PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
memcached 1340 root   26u  IPv4 18958545      0t0  TCP *:memcache (LISTEN)
memcached 1340 root   27u  IPv6 18958546      0t0  TCP *:memcache (LISTEN)
memcached 1340 root   28u  IPv4 18958549      0t0  UDP *:memcache
memcached 1340 root   29u  IPv4 18958549      0t0  UDP *:memcache
memcached 1340 root   30u  IPv4 18958549      0t0  UDP *:memcache
memcached 1340 root   31u  IPv4 18958549      0t0  UDP *:memcache
memcached 1340 root   32u  IPv6 18958550      0t0  UDP *:memcache
memcached 1340 root   33u  IPv6 18958550      0t0  UDP *:memcache
memcached 1340 root   34u  IPv6 18958550      0t0  UDP *:memcache
memcached 1340 root   35u  IPv6 18958550      0t0  UDP *:memcache
  
測試一下memcached鏈接,以下說明成功(輸入quit退出)
[root@mem-node1 ~]# telnet 192.168.10.203 11211
Trying 192.168.10.203...
Connected to 192.168.10.203.
Escape character is '^]'.

5)配置Tomcat,經過MSM實現共享session(192.168.10.201和192.168.10.202機器上操做)
MSM(memcached session manager), MSM是一款實現Tomcat會話保持的管理組件, 支持粘性和無粘性的配置, 目前能夠在Tomcat6,7,8中使用, 而且支持Memcached會話故障轉移.提早下載MSM的類庫文件到/usr/local/src目錄下,下載地址

[root@Tomcat-node1 ~]# cd /usr/local/src/MSM_Software
[root@Tomcat-node1 MSM_Software]# ll
total 1212
-rw-rw-r--. 1 root root  53259 Aug 27 09:53 asm-5.2.jar
-rw-rw-r--. 1 root root 323740 Aug 27 09:51 kryo-4.0.0.jar
-rw-rw-r--. 1 root root  85217 Aug 27 09:51 kryo-serializers-0.38.jar
-rw-rw-r--. 1 root root 152401 Aug 27 09:49 memcached-session-manager-1.9.7.jar
-rw-rw-r--. 1 root root  10788 Aug 27 09:49 memcached-session-manager-tc8-1.9.7.jar
-rw-rw-r--. 1 root root   5711 Aug 27 09:52 minlog-1.3.0.jar
-rw-rw-r--. 1 root root  37160 Aug 27 09:51 msm-kryo-serializer-1.9.7.jar
-rw-rw-r--. 1 root root  51287 Aug 27 09:53 objenesis-2.4.jar
-rw-rw-r--. 1 root root  20883 Aug 27 09:52 reflectasm-1.11.3.jar
-rw-rw-r--. 1 root root 472838 Aug 27 09:50 spymemcached-2.12.2.jar

特別注意:
memcached-session-manager-tc8-1.9.7.jar中的tc8爲tomcat的版本號。
必定要注意:不一樣版本號的tomcat,對應的msm包也不一樣。此處爲tomcat8的jar包。

須要把上面這些MSM依賴的jar包下載後所有上傳到兩臺機器的tomcat安裝路徑的lib/ 目錄下
[root@Tomcat-node1 MSM_Software]# \cp -rf /usr/local/src/MSM_Software/* /usr/local/tomcat8/lib/

接下來進行序列化tomcat配置,序列化tomcat配置的方法有不少種:
java默認序列化tomcat配置、javolution序列化tomcat配置、xstream序列化tomcat配置、flexjson序列化tomcat配置和kryo序列化tomcat配置。
官網介紹說 使用kryo序列化tomcat的效率最高,因此這裏只介紹kryo序列化。
  
在No-Stick模式和Stick模式下context.xml文件配置也有所不一樣(通常用的是No-Stick模式)
只須要修改conf/context.xml文件:
[root@Tomcat-node1 ~]# cd /usr/local/tomcat8/conf/
[root@Tomcat-node1 conf]# cp context.xml context.xml.bak


a)No-Stick模式
記住:多個tomcat實例時 須要選擇Non-Sticky模式,即sticky="false"
[root@Tomcat-node1 conf]# vim context.xml                       #在<Context>和</Context>之間添加下面內容.就在底部</Context>以前添加就行
.......
    <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
         memcachedNodes="n1:192.168.10.203:11211,n2:192.168.10.205:11211"
         lockingMode="auto"
         sticky="false"
         sessionBackupAsync="false"
         sessionBackupTimeout= "1000"   
         copyCollectionsForSerialization="true"
         requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
         transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
    />

第一臺tomcat節點的congtext.xml配置好以後,再將該文件拷貝到另外一臺tomcat節點的相同路徑下


b) Stick模式。
故障轉移配置節點(failoverNodes),不能使用在Non-Sticky模式,多個使用空格或逗號分開,配置某個節點爲備份節點。
當其餘節點都不可用時纔會存儲到備份節點,適用於sticky模式(即一臺tomcat,多臺memcached)。
[root@Tomcat-node1 conf]# vim context.xml                
......
    <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
             memcachedNodes="n1:192.168.10.203:11211,n2:192.168.10.205:11211"            #多個memcached之間用空格或逗號隔開均可以的
             sticky="true"
             failoverNodes="n2"                                            
             requestUriIgnorePattern=".*\.(png|gif|jpg|css|js|swf|flv)$"
             transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
             copyCollectionsForSerialization="true"
    />

第一臺tomcat節點的congtext.xml配置好以後,再將該文件拷貝到另外一臺tomcat節點的相同路徑下,並將failoverNodes後面的參數改成n1
 
配置好以後,必定要記得重啓兩臺機器的tomcat服務!
[root@Tomcat-node1 ~]# /usr/local/tomcat8/bin/shutdown.sh      #或者直接使用kill殺死
[root@Tomcat-node1 ~]# lsof -i:8080
[root@Tomcat-node1 ~]# /usr/local/tomcat8/bin/startup.sh


======================================================================================
Manager 各參數說明:
memcachedNodes     必選項,memcached的節點信息,多個memcached節點,中間須要使用空格
 
failoverNodes="n2"  表示當前session保持到n1的memcached節點上
failoverNodes      可選項,不能使用在non-sticky sessions模式。故障轉移配置節點,多個使用空格或逗號分開,配置某個節點爲備份節點,
當其餘節點都不可用時纔會存儲到備份節點,官方建議配置爲和tomcat同服務器的節點。
理由以下:
假若有兩臺服務器m1,m2,其中m1部署tomcat和memcached節點n1,m2部署memcached節點n2。
若是配置tomcat的failoverNodes值爲n2或者不配置,則當服務器m1掛掉後n1和tomcat中保存的session會丟失,而n2中未保存或者只保存了部分session,
這就形成 部分用戶狀態丟失。
若是配置tomcat的failoverNodes值爲n1,則當m1掛掉後由於n2中保存了全部的session,因此重啓tomcat的時候用戶狀態不會丟失。
爲何n2中保存了全部的session? 由於failoverNodes配置的值是n1,只有當n2節點不可用時纔會把session存儲到n1,因此這個時候n1中是沒有保存任何session的。
 
lockingMode  可選值,默認none,只對non-sticky有效。
 
requestUriIgnorePattern  可選值,制定忽略那些請求的session操做,通常制定靜態資源如css,js一類的。
 
sessionBackupAsync    可選值,默認true,是否異步的方式存儲到memcached。
 
sessionBackupTimeout  可選項,默認100毫秒,異步存儲session的超時時間。

6) MSM會話共享測試
a) 訪問http://www.kevin.com,按ctrl+F5強刷頁面,發現session信息會變,可是sessionid不會改變!說明session實現了共享! 以下,表示當前sessionid保存到了n1這個memcached節點上了.

b) 關閉Mem-node1節點的memcached服務,繼續訪問頁面,發現sessionid保存到了n2這個memcached節點上了,可是sessionid任然沒有改變,說明session已共享. 也就是說,關閉memcached集羣中的任意一個節點.訪問頁面,sessionid都不會改變.即能夠實現memcached故障轉移!以下:

c) 關閉tomcat-node1和tomcat-node2中的任意一個節點的tomcat服務,繼續訪問頁面,發現前端從nginx負載過來的請求達到未關閉的tomcat節點上,sessionid都不會改變,任然在共享中!即能夠實現tomcat故障轉移

關閉tomcat-node1節點的tomcat服務,繼續訪問頁面:

關閉tomcat-node2節點的tomcat服務,繼續訪問頁面:

特別提示:
若是memcached session manager的會話共享配置後,重啓tomcat服務沒有報錯,可是訪問頁面的時候報錯,頁面訪問失敗,以下在logs/catalina.out日誌裏發現的錯誤:SEVERE [http-nio-8080-exec-1] org.apache.coyote.http11.AbstractHttp11Processor.process Error processing request java.lang.NoSuchFieldError: attributes

這種錯誤狀況基本就是jar包版本不兼容致使的,須要到這裏下載跟tomcat版本相對應的jar包!!

相關文章
相關標籤/搜索