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
相关文章
相关标签/搜索