这里主要介绍下后面两种方式。
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
将下面这段配置添加到每个节点的主配置文件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>在对应的站点目录下,创建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没变,所以实现了会话保持。
原理:Tomcat会将会话信息保存在Memcached中,当有用户访问时,就可以直接在Memcached中找会话信息。不用基于前端的负载均衡器绑定ip,也不需要tomcat集群互相复制会话信息。 项目地址:https://github.com/magro/memcached-session-manager 项目文档:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration
下载如下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目录下
这里的序列化工具有许多种
kryo-serializer: msm-kryo-serializer, kryo-serializers-0.34+, kryo-3.x, minlog, reflectasm, asm-5.x, objenesis-2.xjavolution-serializer: msm-javolution-serializer, javolution-5.4.3.1xstream-serializer: msm-xstream-serializer, xstream, xmlpull, xpp3_minflexjson-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>在两个tomcat节点安装Memcached并启动
yum install memcached -y systemctl start memcached在刷新一次
这时候可以看到使用的是n2这个主机的Memcached,这时候我们down了n2(192.168.253.158)的Memcached
可以看到Session ID没变,但是后面变成了n1节点,证明了即使n2宕了,n1也能接管服务
