Nginx+Tomcat的Session集羣與旁掛式memcached實現

自2006年後Sun分拆Java技術爲三個方向:Java 2 SE(標準版)  Java 2 EE(企業版)  Java 2 ME(移動端)  
css

TOMCAT是Java 2 EE技術體系的不完整實現,不少API仍然不能和J2EE相比擬,因爲Sun在後TOMCAT算是Apache基金會中和apache並驅前行的頂級項目,最新的Servlet 和JSP 規範老是能在Tomcat 中獲得體現。html

但TOMCAT並不是惟一的選擇,由於Tomcat 技術先進、性能穩定,並且免費,於是深受Java 愛好者的喜好並獲得了部分軟件開發商的承認,成爲目前比較流行的JAVA應用服務器。前端


商業實現:java

WebSphere, WebLogic, Oc4j, Glassfish, Geronimo, JOnAS, JBoss, ...node

開源實現:linux

Tomcat, Jetty, Resin, ...nginx


其中JSP在Tomcat中運行由jasper將.jsp內容自動轉化爲java代碼並加載入類庫git

.jsp -->jasper--> .java --> javac --> .class --> jvm   ## 注意:基於jasper將靜態輸出的數據轉爲java代碼進行輸出,結果爲servlet規範的代碼;

今天主要爲了實現Tomcat的會話保持,也就是會話粘性,其實實現Tomcat Cluster或者TOmcat+memcached以前就有不少方法,但大致上分爲github


    代理端會話粘性:若其中一個代理出現問題,會話粘性單點失效web

            nginx: ip_hash

            haproxy: source

            lvs: sh


    集羣會話同步:Tocmat會話兩兩互爲同步,單並不適合大流量,Tomcat性能降低

            Tomcat: delta session manager


    會話共享:高效,但配置較爲麻煩依賴於第三方插件,淡然還有不少方案,只是這裏不一一推薦

            Tomcat+Memcached/Redis


今天主要研究Tomcat+Memcached,基本環境部署階段各個節點應先進行時間同步


--> 基本環境:

        192.168.2.128   node1   Nginx

        192.168.2.129   node2   Tomcat1

        192.168.2.130   node3   Tomcat2


本次環境涉及到的軟件包與插件雲地址:https://pan.baidu.com/s/1ZL4gLbOdL2F56sDlmpFBwg  提取碼:j3m7


安裝Tomcat

    安裝Tomcat以前首先安裝jdk環境,jdk分爲openjdk和Oracle jdk,OracleJDK做爲openjdk的穩定功能收錄版



yum安裝方式:

yum install java-1.8.0-openjdk java-1.8.0-openjdk-devel ##java-1.8.0-openjdk-devel中包含了一些jvm狀態分析工具,好比jps和jstat 
yum install tomcat tomcat-lib tomcat-admin-webapps  tomcat-webapps tomcat-docs-webapp


Tar包安裝:

                  1. 安裝JDK:

[root@node2 ~]# rpm -ivh jdk-8u201-linux-x64.rpm
[root@node2 profile.d]# cat /etc/profile.d/jdk.sh 
	export JAVA_HOME=/usr/java/default
	export PATH=$JAVA_HOME/bin:$PATH
[root@node2 profile.d]# . /etc/profile.d/jdk.sh

                  2. 安裝Tomcat:

[root@node2 ~]# tar xf apache-tomcat-7.0.92.tar.gz -C /usr/local/
[root@node2 local]# ln -s apache-tomcat-7.0.92 tomcat
[root@node2 profile.d]# cat tomcat.sh 
	export TOMCAT_HOME=/usr/local/tomcat
	export PATH=$TOMCAT_HOME/bin:$PATH
[root@node2 profile.d]# . /etc/profile.d/tomcat.sh


               3. 配置Tomcat,編寫jsp

[root@node2 ~]# mkdir -pv /usr/local/tomcat/webapps/test/{classes,lib,META-INF,WEB-INF}
[root@node2 ~]# cat /usr/local/tomcat/webapps/test/index.jsp

<%@ page language="java" %>
<html>
		<head><title>TomcatB</title></head>
				<body>
				<h1><font color="blue">TomcatB.ifan.com</font></h1>
				<table align="centre" border="1">
						<tr>
								<td>Session ID</td>
						<% session.setAttribute("ifan.com","ifan.com"); %>
								<td><%= session.getId() %></td>
						</tr>
						<tr>
								<td>Created on</td>
								<td><%= session.getCreationTime() %></td>
						</tr>
				</table>
				</body>
</html>

                4. 檢查配置文件並啓動服務:

[root@node2 test]# catalina.sh configtest
[root@node2 test]# catalina.sh start

            檢查端口:8080  8009  8005(啓動較慢)

            分別訪問192.168.2.129:8080和192.168.2.130:8080以及http://url:8080/test正常便可



因爲與TOMCAT默認通信的方式有兩種:

                http協議(默認8080端口)支持nginx,apache等http協議前端服務器

                ajp協議(默認8009端口)僅Apache支持,若是不使用則能夠關閉

另外的8005端口爲Tomcat的管理端口,聽說還有jk的連接方式,不過這個項目好像已經被apache基金會廢棄了,不在研究

        

    安裝NGINX:

[root@node1 ~]# yum install nginx -y
[root@node1 ~]# cat /etc/nginx/conf.d/http-tomcat.conf

    配置NGINX服務:

[root@node1 ~]# cat /etc/nginx/conf.d/nginx_tomcat.conf 
	upstream tcsrv {
	#    ip_hash;               ##這裏採用Tomcat Cluster的會話複製來實現就再也不使用ip_hash,使用基本輪訓便可
		server 192.168.2.129:8080 weight=1;
		server 192.168.2.130:8080 weight=1;
	}
	
	server{
			listen 80;
			server_name http.ilinux.io;
			proxy_set_header X-Forwarded-For $remote_addr;      ##將用戶客戶端真實IP傳遞給Tomcat,httpd2.4無需配置透傳,Tomcat自動得到的就是真實IP
			location / {
			proxy_pass http://tcsrv;
			}
	
	}

    配置ubuntu本地hosts:

        ifan@ifan-PC:~$ cat /etc/hosts

            127.0.0.1localhost

            0.0.0.0 account.jetbrains.com 

            127.0.1.1   ifan-PC

            192.168.2.128 http.ilinux.io ajp.ilinux.io

    測試訪問:

            http://http.ilinux.io   可以輪訓看到TomcatA和B便可


image.png



Tomcat Cluster集羣會話同步配置:



拓撲圖:


image.png

1. 在開始以前咱們首先備份一下配置文件:

[root@node2 ~]# cp /usr/local/tomcat/conf/server.xml{,.bak}

2. 編輯集羣內每臺tomcat主配置文件server.xml在須要配置的Host標籤內添加如下信息

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
		channelSendOptions="8">

<Manager className="org.apache.catalina.ha.session.DeltaManager"
		expireSessionsOnShutdown="false"
		notifyListenersOnReplication="true"/>

<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
				address="228.0.0.4"         <!-- 在同一個組播地址則認爲在一個集羣範圍內 -->
				port="45564"
				frequency="500"
				dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
		address="auto"          <!-- auto須要將本機主機名在本機的hosts內作ip映射,不然會bind錯誤 -->
		port="4000"
		autoBind="100"
		selectorTimeout="5000"
		maxThreads="6"/>          <!-- 最大線程數,能夠理解爲集羣服務器總數爲N,則該值爲N-1 -->

<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
		filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
		tempDir="/tmp/war-temp/"
		deployDir="/tmp/war-deploy/"
		watchDir="/tmp/war-listen/"
		watchEnabled="false"/>

<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

3. 在test項目單獨複製web.xml並在web-app標籤內配置<distributable/>

[root@node2 ~]# cp /usr/local/tomcat/conf/web.xml /usr/local/tomcat/webapps/test/WEB-INF/
[root@node2 ~]# vim /usr/local/tomcat/webapps/test/WEB-INF/web.xml
	<web-app>
		...
		<distributable/>
		...
	web-app/>

4.  配置tomcat主機對本機主機名hosts解析

[root@node2 ~]# cat /etc/hosts
   192.168.2.129 node2
[root@node2 ~]# cat /etc/hosts
   192.168.2.130 node3

        

5. 檢測配置,並啓動服務:

[root@node2 ~]# catalina.sh configtest
[root@node2 ~]# catalina.sh start;tailf /usr/local/tomcat/logs/catalina.2019-02-22.log


6. 再次訪問http://http.ilinux.io/test,能夠留意到Session ID 是一致的,這就說明當前端HA/NGINX/LVS不管如何調度到集羣中任何主機,那麼會話都是持續保持的


        image.png

image.png


這就是Tomcat自身集羣實現的Session共享功能,可是因爲這種Tomcat自己性能就比較低,集羣自己實現相互複製就更是雪上加霜,因此不建議在併發時或者集羣數量多時使用

這裏建議使用旁掛式Memcache/Redis方案


咱們緊接着上述實驗來實現Nginx+Tomcat+Memcached


環境:

        192.168.2.128   node1   Nginx

        192.168.2.129   node2   Tomcat1+Memcached

        192.168.2.130   node3   Tomcat2+Memcached



實現這種旁掛式須要Tomcat鏈接Memcached的擴展,這裏藉助第三方的插件來實現,

訪問全球最大同×××流社區github,具體連接:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration

拓撲圖:

image.png

這種原理能夠理解爲

客戶端請求到達前端nginx調度器並分配到後端某tomcat節點時,tomcat會優先使用本機內存保存session,當一個請求結束後,tomcat會經過第三方組件(kryo,javolution,xstream,flexjson)把session序列化併發送到memcached節點上存放做備份,第二次請求時,若是本地有session就直接返回,第二次請求結束,把session修改後的信息更新到後端的memcached服務器,以這樣的方式來保持本地的session與memcached上的session同步。當這個tomcat節點宕機時,那麼用戶的下一次請求就會被前端的負載均衡器路由到另外一個tomcat節點上,而這個節點上並無這個用戶的session信息,這個節點就從memcached服務器上去讀取session,並把session保存到本地的內存,當請求結束,session又被修改,再送回到memcached進行存放備份

當後端配置了多臺memcached時,tomcat在更新session信息時會同時向多個memcached節點更新session,當一個memcached節點故障時,tomcat能夠從選擇一個正常工做的memcached節點讀取session信息來發送給用戶的瀏覽器,讓其重置session信息,這樣,memcached也達到了高可用的目的;



在開始以前首先進行序列化器的選擇,我這裏使用javolution,插件能夠在github上去下載;


複製插件到Tomcat的lib目錄中:

[root@node1 msm]# scp *.jar root@192.168.2.129:/usr/local/tomcat/lib/
[root@node1 msm]# scp *.jar root@192.168.2.130:/usr/local/tomcat/lib/

	javolution-5.5.1.jar
	memcached-session-manager-2.1.1.jar
	memcached-session-manager-tc7-2.1.1.jar
	msm-javolution-serializer-2.1.1.jar
	spymemcached-2.11.6.jar

而後備份集羣server.xml文件:

[root@node2 ~]# mv /usr/local/tomcat/conf/server.xml{,.cluster}

還原Tomcat配置文件爲初始文件,並在Host標籤中添加如下信息:

[root@node2 ~]# cp /usr/local/tomcat/conf/server.xml.bak /usr/local/tomcat/conf/server.xml
[root@node2 ~]# vim /usr/local/tomcat/conf/server.xml
...
<Context path="/test" docBase="test" reloadable="true">
        <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
                memcachedNodes="m1:192.168.2.129:11211,m2:192.168.2.130:11211"
                failoverNodes="m1"      <!-- 當m2節點失效時,使用m1節點 -->
                requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"   <!-- 如下後綴不進行會話保持 -->
                transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"/>
        </Context>
...

配置完成後,檢測配置並重啓Tomcat便可

再次查看是否可以會話共享

image.png

image.png


這樣便可實現了旁掛式session共享,當後端memcached出現故障時會馬上將使用另一臺memcached,各個Tomcat節點再將本地的session保存到新的memcached

最後說一下關於插件和Tomcat有一些是不兼容的,這裏也貼出個人配置信息,若是和個人tomcat不匹配則可能須要去https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration下載其餘版本

[root@node4 ~]# catalina.sh version 
Server version: Apache Tomcat/7.0.92
Server built:   Nov 9 2018 11:07:44 UTC
Server number:  7.0.92.0
OS Name:        Linux
OS Version:     3.10.0-327.el7.x86_64
Architecture:   amd64
JVM Version:    1.8.0_201-b09
JVM Vendor:     Oracle Corporation
相關文章
相關標籤/搜索