最近项目做了点小小的改动,web服务器(tomcat)部署在两台机器上(只给提供两台),防止单节点故障,类似于如下架构
这种情况下,之前单机的session已经不能满足了,只能修改为分布式的session共享,考虑了两种方案:
第一种是用spring session + redis实现session共享,这种方案其实很简单,spring已经提供了,只需要在pom中引用对应的spring-session-data-redis的jar就行,但是没办法,领导要求只能是两台电脑,redis集群需要至少三台以上才可用(这个在网上查的,没有实际测试过),而且现在项目中用到的缓存是memcached,所以只能放弃这种方案。
第二种就是tomcat+memcached实现分布式session共享,使用的是开源的memcached-session-manager,一个开源的高可用session解决方案。分为两种模式:Sticky模式和Non-Sticky模式,简单介绍下这两种模式:
Sticky模式(粘性session):tomcat session为主session,memcached为备session。每次请求都会被映射到同一台web服务器,除非该web服务器宕机。这样session其实是存放在服务器的本地的,直到请求处理完成后,才会同步到memcached服务器;而当Web服务器宕机时,请求被映射到其他Web服务器,这时候,其他Web服务器可以从后端memcache中恢复session
Non-Sticky(非粘性session):请求每次映射的后端Web服务器是不确定的,当请求到来时,从memcached中加载session,当请求处理完成时,将session再写回到memcached。
目前我们项目中已经实现了分布式的memcached,现在只需要将tomcat+memcached配置好即可。我使用的是Non-Sticky模式
首先需要导入如下jar包到tomcat的lib目录下,对应的pom如下,严格按照jar的版本添加,不然会因为jar冲突导致各种奇怪的报错
<!--分布式session--> <dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>5.0.3</version> </dependency> <dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>3.0.3</version> </dependency> <dependency> <groupId>de.javakaffee</groupId> <artifactId>kryo-serializers</artifactId> <version>0.37</version> </dependency> <dependency> <groupId>de.javakaffee.msm</groupId> <artifactId>memcached-session-manager</artifactId> <version>1.9.5</version> </dependency> <dependency> <groupId>de.javakaffee.msm</groupId> <artifactId>memcached-session-manager-tc7</artifactId> <version>1.9.5</version> </dependency> <dependency> <groupId>com.googlecode</groupId> <artifactId>minlog</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>de.javakaffee.msm</groupId> <artifactId>msm-kryo-serializer</artifactId> <version>1.9.5</version> </dependency> <dependency> <groupId>com.esotericsoftware.reflectasm</groupId> <artifactId>reflectasm</artifactId> <version>1.09</version> </dependency> <dependency> <groupId>net.spy</groupId> <artifactId>spymemcached</artifactId> <version>2.12.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.objenesis/objenesis --> <dependency> <groupId>org.objenesis</groupId> <artifactId>objenesis</artifactId> <version>2.1</version> <scope>test</scope> </dependency>
然后修改tomcat的conf目录下的server.xml和context.xml,我是在本地虚拟机里面装了两个tomcat指定了两个不同的端口去启动的。
server.xml修改如下:多台web服务器jvmRoute做以区分,如果是本地启动多台tomcat,记得把端口修改为不一样的。
<Engine name="Catalina" defaultHost="192.168.212.37" jvmRoute="tomcat1">
context.xml修改如下:主要是修改Manager节点,如果是分布式的memcached,memcachedNodes可以写成:memcachedNodes="n1:192.168.212.36:11211,n2:192.168.212.37:11211"这种形式,因为我这里引用的是我物理机上的单点memcached,所以只写了如下的配置:
<Context> <!-- Default set of monitored resources --> <WatchedResource>WEB-INF/web.xml</WatchedResource> <!-- Uncomment this to disable session persistence across Tomcat restarts --> <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:192.168.212.36:11211" sticky="false" sessionBackupAsync="false" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|jpeg|bmp|css|js|html|htm)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" /> <!-- Uncomment this to enable Comet connection tacking (provides events on session expiration as well as webapp lifecycle) --> <!-- <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" /> --> </Context>
以上操作完成后,启动tomcat,访问不同的web服务器,在同一浏览器里打印sessionId,效果如下:可以看出两台web服务器实现了session共享
查看另一台机器上的memcached,可以看到已经存入的sessionId
等抽空把spring session + redis这种方案测试一次
更新于2017年09月28日
更正下,之前我做调研是在自己的虚拟机里面启动了两个tomcat,只是端口不同,直接访问这两个tomcat获取到的sessionId是一样的,今天我部署到测试环境上,发现直接访问两个不同机器的tomcat,获取到的sessionId不同,查询memcached里面发现存了两份,一早上都在怀疑是不是自己配置出问题了,最后另一个同事把keepalive配置好后,也就是该博文最上面的架构图,我通过访问192.168.211.216这个虚拟IP地址通过keepalive去切换到实际的两台tomcat服务器192.168.211.218和192.168.211.220,从这两台服务器后台日志可以看出,哪一个挂掉就会切换到另一台上,而且sessionId是一样的。所以上面的测试如果要在不同机器上生效,应该要配置下Nginx去做下转发,后面再仔细研究下吧。