對於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包!!