對於web應用叢集的技術實現而言,最大的難點就是:如何能在叢集中的多個節點之間保持資料的一致性,會話(Session)資訊是這些資料中最重要的一塊。要實現這一點, 大體上有兩種方式:
一種是把所有Session資料放到一臺伺服器上或者資料庫中,叢集中的所有節點通過訪問這臺Session伺服器來獲取資料;
另一種就是在叢集中的所有節點間進行Session資料的同步拷貝,任何一個節點均儲存了所有的Session資料。
在叢集系統下實現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共享和高可用。
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。這種方式配置比較複雜。
在Tomcat叢集中,當一個節點出現故障,雖然有高可用叢集來負責故障轉移,但使用者的session資訊如何保持呢?
下面介紹第4種方案,session複製同步使用MSM(Memcache-Session-Manager),即利用MSM+Memcached做Session共享。
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等進行了指定。
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過期為止。
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)。
為什麼要持久化session(共享session)?
因為:在客戶端每個使用者的Session物件存在Servlet容器中,如果Tomcat伺服器重啟或者當機的話,那麼該session就會丟失,而客戶端的操作會由於session丟失而造成資料丟失;如果當前使用者訪問量巨大,每個使用者的Session裡存放大量資料的話,那麼就很佔用伺服器大量的記憶體,進而致使伺服器效能受到影響。
可以使用資料庫持久化session,分為物理資料庫和記憶體資料庫。物理資料庫備份session,由於其效能原因,不推薦;記憶體資料庫可以使用redis和memcached,這裡介紹memcached的方法。
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備份。
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.131、tomcat8.0.53 8080 192.168.10.202 Tomcat-node2 java8.131、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包!!