Tomcat实现集群的Session会话共享
Tomcat有提供自己实现会话共享的方式,这里也是主要介绍Tomcat提供了的实现。Tomcat官方提供的实现主要是中间件层的改动,不需要对项目已有的代码改动。对于实现Session共享官方文档的介绍已经很清晰了。
一、配置server.xml文件
server.xml
文件的修改主要是增加Cluster
节点,基本按照官方的配置,在Host节点中添加下面的配置。
<!-- 如果使用群集,应用的web.xml要加上distributable标签 -->
<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" />
<!-- 在群集中 本服务器用于接收来自其他服务器分发的信息 例如同步会话 -->
<!-- 若在同一台机器上运行两个Tomcat实例 需要修改这个端口 具体数值无要求 不重复即可 -->
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" port="4001" 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" />
</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>
<!-- 结束配置群集 -->
Tomcat需要session共享,需要注意的是,session中的属性必须实现java.io.Serializable
。
二、配置web.xml
主要是在需要集群的项目中,配置web.xml,主要是添加一个元素节点<distributable/>
即可。如下web.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
<display-name>Welcome to Tomcat</display-name>
<description>
Welcome to Tomcat
</description>
<!--要实现session复制,分布式web容器应用 -->
<distributable/>
</web-app>
三、配置实例
简单的实践一下,没有去写个新项目,直接使用了Tomcat的ROOT项目,在index.jsp
文件中写了一段获取session的的JSP代码,如下:
<div id="middle" class="curved container">
<b>使用的是mod_proxy的方式实现负载均衡,HTTP协议!</b>
Tomcat2</br>
<% String sessionStr=session.getId();%>
<% //默认每一次刷新都会加1
Object obj = session.getAttribute("globalValue");//默认的不断加1的值
Integer globalValue = 0;
if(obj == null){
globalValue = 1;
session.setAttribute("globalValue",globalValue);
}else{//如果会话中已经存在则进行加1
globalValue = (Integer)obj;
globalValue = globalValue + 1;
session.setAttribute("globalValue",globalValue);
}
%>
当前的sessionId:<b><%=sessionStr %></b> <br>
Session中的globalValue(每刷新一次会自动加1):<b><%=globalValue %></b> <br>
</div>
还有一点,ROOT项目的index.jsp中默认是禁用了session的,需要在page指令允许当前jsp使用session
,如下所示:
<%@ page session="true" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
这里使用的是三个Tomcat一起实现session共享,所以以上的配置在每个tomcat中都需要配置,分别是Tomcat0、Tomcat1、Tomcat2;启动每一个tomcat,访问各个Tomcat的地址,可以看到它们之间的Session是相同的,sessionid都是:BC494768E884D01126B5102A643D541E
,此外在session中存储的globalValue会每次都加1。
Tomcat0的访问截图,globalValue=1
:
Tomcat1的访问截图,globalValue=2
:
Tomcat2的访问截图,globalValue=3
:
测试的项目使用的是Tomcat自带的ROOT项目,在Tomcat和这个ROOT项目中配置了session共享的配置;3个Tomcat的服务器中的ROOT项目的session实现了会话共享,因为如上的三次访问分别访问了Tomcat0,Tomcat1,Tomcat2,它们的sessionId都是一样的,而且存储在Session中的globalValue的值时每访问一次都只加1,表明globalValue在3个Tomcat的ROOT项目中是共享的,才能实现这样的效果。
四、总结
实现session共享,官方也提供几种实现session共享方式,无非是将session存在数据库和将session存在内存中。而这篇主要是Tomcat自带的内存session共享
的方式,是一种All To All
的方式,比较耗费资源。对于小集群来说还是不错的,如果是大集群将会不合适了。Tomcat服务器之间的session复制或者说共享是有两种传递方式,组播的方式和单播的方式
,一般推荐组播的方式,但是有时候某些机器不支持组播则只能采用单播。
遇到的问题:
Tomcat提供的session共享是基于组播实现的,当我尝试使用物理机和虚拟机的tomcat建立集群时,出现了tomcat的session无法共享的问题。他们之间的网络是互通的,不知是否是端口或者防火墙的策略问题。