Tomcat会话保持的几种方式
- 利用Nginx和Apache反向代理时使用源地址hash算法,将相同源地址的请求发送到相同的一台后端Tomcat服务器
- 利用Tomcat自带的集群工具实现会话绑定,这个方式会让每个Tomcat主机把自己的会话信息共享到其他Tomcat主机。不宜过多,4,5个节点就已经很大了。
- 将会话信息保存到Memcached,当用户访问的时候,会把会话信息保存到一个非关系型数据库Memcached中,当再次访问的时候,Tomcat会到数据库找会话记录,实现会话保持。
这里主要介绍下后面两种方式。
环境准备
ip | 角色 | 安装的软件 |
---|---|---|
192.168.253.128 | TomcatA | Tomcat |
192.168.253.158 | TomcatB | Tomcat |
192.168.253.158 | Nginx反代 | Nginx |
TomcatA的测试页面
<%@ page language="java" %>
<html>
<head><title>TomcatA</title></head>
<body>
<h1><font color="red">TomcatA</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("TomcatA","TomcatA"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>
TomcatB测试页面
<%@ page language="java" %>
<html>
<head><title>TomcatB</title></head>
<body>
<h1><font color="red">TomcatB</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("TomcatB","TomcatB"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>
访问两个Tomcat节点初始页,出现下图则成功。其中Session ID就是我们的会话ID,如果实现了同一个主机访问得到相同的Session ID则可以认为会话绑定成功。
Nginx反向代理配置文件
[root@localhost ~]# cat /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
upstream tomcat {
server 192.168.253.128:8080 weight=1;
server 192.168.253.158:8080 weight=1;
}
server {
listen 80 default_server;
location / {
proxy_pass http://tomcat ;
}
}
}
这里的设置的算法是轮询,权重是1比1
Tomcat Clustering
第一步:修改配置文件server.xml
将下面这段配置添加到每个节点的主配置文件server.xml的 或 组件中
<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="ip" #这里的ip是集群内部通信监听的ip地址
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<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.MessageDispatchInterceptor"/>
</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.ClusterSessionListener"/>
</Cluster>
上面配置的注释
<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"> #channel定义信道
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4" #多播地址通信
port="45564" #多播地址使用的端口,监听再这个端口上的就是同一个集群成员
frequency="500" #每隔0.5s发心跳探测
dropTime="3000"/> #3s没收到心跳探测认为他down了
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" #接受信息,java内部的一个异步处理逻辑
address="ip" #这里的ip是集群内部通信监听的ip地址
port="4000" #接受对应信息的端口
autoBind="100" #每隔多长时间自动绑定一次
selectorTimeout="5000" #选择器的超时时间
maxThreads="6"/> #最大并发连接数
<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.MessageDispatchInterceptor"/>
</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.ClusterSessionListener"/>
</Cluster>
第二步:配置webapps
在对应的站点目录下,创建WEB-INF/web.xml文件,添加元素;
因为是测试所以比较简单直接复制全局的web.xml,然后再里面添加元素就好了
mkdir /data/test/WEB-INF
cp /usr/local/tomcat/conf/web.xml /data/test/WEB-INF/ #复制默认的web.xml到对应的站点目录下
/data/test/WEB-INF/web.xml 中添加下面一行
<distributable/>
两台tomcat节点重启一下tomcat
catalina.sh stop
catalina.sh start
可以看到Session ID没变,所以实现了会话保持。
将Session保存在Memcached中
原理:Tomcat会将会话信息保存在Memcached中,当有用户访问时,就可以直接在Memcached中找会话信息。不用基于前端的负载均衡器绑定ip,也不需要tomcat集群互相复制会话信息。
项目地址:https://github.com/magro/memcached-session-manager
项目文档:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration
第一步:下载memcached-session-manager会话管理工具
下载如下jar文件至各tomcat节点的tomcat安装目录下的lib目录中
其中的${version}要换成你所需要的版本号,tc${6,7,8}要换成与tomcat版本相同的版本号。
memcached-session-manager-${version}.jar
memcached-session-manager-tc${6,7,8}-${version}.jar
spymemcached-${version}.jar
这里节点是tomcat8.5,具体版本为
memcached-session-manager-1.9.6.jar #下载地址 http://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager/1.9.6/
memcached-session-manager-tc8-1.9.6.jar #下载地址http://repo1.maven.org/maven2/de/javakaffee/msm/memcached-session-manager-tc8/1.9.6/
spymemcached-2.12.2 #下载地址 http://repo1.maven.org/maven2/net/spy/spymemcached/2.12.2/
将这些文件下载到tomcat的lib目录下
第二步:添加序列化序列化工具javolution-serializer
这里的序列化工具有许多种
- kryo-serializer: msm-kryo-serializer, kryo-serializers-0.34+, kryo-3.x, minlog, reflectasm, asm-5.x, objenesis-2.x
- javolution-serializer: msm-javolution-serializer, javolution-5.4.3.1
- xstream-serializer: msm-xstream-serializer, xstream, xmlpull, xpp3_min
- flexjson-serializer: msm-flexjson-serializer, flexjson
这次使用的序列化工具是javolution-serializer,具体版本如下
msm-javolution-serializer #下载地址 http://repo1.maven.org/maven2/de/javakaffee/msm/msm-javolution-serializer/1.9.6/
javolution-5.4.3.1 #下载地址 http://www.java2s.com/Code/Jar/j/Downloadjavolution5431jar.htm
将这些文件下载到tomcat的lib目录下
第三步:修改配置文件
分别在两个tomcat上的某host上定义一个用于测试的context容器,并在其中创建一个会话管理器,如下所示:
<Context path="" docBase="/data/test" reloadable="true" >
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:192.168.253.128:11211,n2:192.168.253.158:11211"
failoverNodes="n1"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"
/>
</Context>
第四步:安装Memcached
在两个tomcat节点安装Memcached并启动
yum install memcached -y
systemctl start memcached
第五步:重启Tomcat并测试
catalina.sh stop
catalina.sh start
在刷新一次
这时候可以看到使用的是n2这个主机的Memcached,这时候我们down了n2(192.168.253.158)的Memcached
可以看到Session ID没变,但是后面变成了n1节点,证明了即使n2宕了,n1也能接管服务