自2006年后Sun分拆Java技术为三个方向:Java 2 SE(标准版)  Java 2 EE(企业版)  Java 2 ME(移动端)  

TOMCAT是Java 2 EE技术体系的不完整实现,很多API仍然不能和J2EE相比拟,由于Sun在后TOMCAT算是Apache基金会中和apache并驱前行的顶级项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现。

但TOMCAT并非唯一的选择,因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的JAVA应用服务器。


商业实现:

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

开源实现:

Tomcat, Jetty, Resin, ...


其中JSP在Tomcat中运行由jasper将.jsp内容自动转化为java代码并加载入类库

.jsp -->jasper--> .java --> javac --> .class --> jvm   ## 注意:基于jasper将静态输出的数据转为java代码进行输出,结果为servlet规范的代码;

今天主要为了实现Tomcat的会话保持,也就是会话粘性,其实实现Tomcat Cluster或者TOmcat+memcached之前就有很多方法,但大体上分为


    代理端会话粘性:若其中一个代理出现问题,会话粘性单点失效

            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" %>

		TomcatB
				
				

TomcatB.ifan.com

Session ID <% session.setAttribute("ifan.com","ifan.com"); %> <%= session.getId() %> Created on <%= session.getCreationTime() %>

                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即可


Nginx+Tomcat的Session集群与旁挂式memcached实现_第1张图片



Tomcat Cluster集群会话同步配置:



拓扑图:


Nginx+Tomcat的Session集群与旁挂式memcached实现_第2张图片

1. 在开始之前我们首先备份一下配置文件:

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

2. 编辑集群内每台tomcat主配置文件server.xml在需要配置的Host标签内添加以下信息







				port="45564"
				frequency="500"
				dropTime="3000"/>

		port="4000"
		autoBind="100"
		selectorTimeout="5000"
		maxThreads="6"/>          















3. 在test项目单独复制web.xml并在web-app标签内配置

[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/>

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无论如何调度到集群中任何主机,那么会话都是持续保持的


        Nginx+Tomcat的Session集群与旁挂式memcached实现_第3张图片

Nginx+Tomcat的Session集群与旁挂式memcached实现_第4张图片


这就是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

拓扑图:

Nginx+Tomcat的Session集群与旁挂式memcached实现_第5张图片

这种原理可以理解为

客户端请求到达前端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 [email protected]:/usr/local/tomcat/lib/
[root@node1 msm]# scp *.jar [email protected]:/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
...

        
                requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"   
                transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"/>
        
...

配置完成后,检测配置并重启Tomcat即可

再次查看是否能够会话共享

Nginx+Tomcat的Session集群与旁挂式memcached实现_第6张图片

Nginx+Tomcat的Session集群与旁挂式memcached实现_第7张图片


这样即可实现了旁挂式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