1 起因
最近对新开发的web系统进行了压力测试,发现tomcat默认配置下压到600人的并发登录首页响应速度就有比较严重的影响,一轮出现2000多个的500和502错误。我把登录的时间统计做了一下,把服务器处理总时间打印出来,看了一下发现有个别响应确实在20秒,但平均时间和lr测试出来的还是相差很远。所以可以断定不是程序处理处理花费了这么多时间,由于在局域网测试,所以也可以排除网络问题。这就把问题圈定在tomcat的请求响应能力上了。先把tomcat线程数提升到1000,发现500和502的报错降到几十个,但是响应时间上还没什么提高。后来启动了2个tomcat,用nginx做负载均衡,响应时间下降了40%,两个tomcat的处理时长都保持在1秒左右。
看来tomcat性能确实是系统的一个瓶颈,很有必要假设多个服务器来加强响应能力。之前由于只是测试登录,多个tomcat还不用共享session,但真正使用时是必须要能一起工作的。现记录一下负载均衡的安装配置过程。
2 解决方案的选择
多个tomcat要一起协同工作有几种办法,可以考虑的方案有以下几个:
1. 使用tomcat自带的cluster方式,多个tomcat见自动实时复制session信息,配置起来很简单。但这个方案的效率比较低,在大并发下表现并不好。
2. 利用nginx的基于访问ip的hash路由策略,保证访问的ip始终被路由到同一个tomcat上,这个配置更简单。但是我们的应用很可能是某一个局域网大量用户同时登录,这样负载均衡就没什么作用了。
3. 利用memcached把多个tomcat的session集中管理,这是最直接的解决方案,但是操作起来也最为复杂。基于缓存的session管理。
我们的系统既要求性能,又要比较好的利用上负载均衡,所以第3个方案是首选。接下来就是安装搭建之路了。
3 sticky session或non-sticky session的选择
1. 如何分散访问请求到集群的各个结点
2. 如何通过一种session管理策略,确保某一个结点失效后,其session数据能由其他结点获取以便其他结点接替失效结点,实现集群的容错(failover)
对于第一个问题,最简单最直白的想法当然是均匀散列请求到各结点,但是对于应用服务器而言,由于有session的存在,一种更好的处理方式是同一个session的相关请求分发到同一个结点进行处理,这就是所谓的“粘性ssession”(sticky session)方式的负载均衡,而前者就称之为:“非粘性ssession”(non-sticky session)方式的负载均衡.
对于第二个问题,多数的应该服务器(包括Tomcat在内)使用的是session复制(session replication)机制,即结点之间通过组播方式将各自的session发到其他所有结点上,如果其中一个访问出错,则另外结点仍然具有有效的session内容,从而能正常接管其session。由于服务器内置了session复制机制的实现,因而使用这种方案非常简单,只需要做简单的配置即可完成,但是其缺点也是很明显的,由于大量的session信息需要复制,在用户数量和集群数量达到一定规模后,session复制就有可能成为性能瓶颈。于是人们想到了别外一种解决策略:通过第三方缓存来存放session数据
,如果某一结点失效,被委任接替失效结点的服务器可以从缓存中恢复session.基于这种思想,在google code上有一款开源产品:memcached-session-manager。
4 搭建tomcat集群利用msm(memcached-session-manager)来实现共享
一: 搭建好nginx+tomcat的负载均衡(轮询)
架构说明
nginx (100,101上面都有实现动静分离)
|
—— ——
| |
| |
tomcat1 tomcat2
192.168.1.100 192.168.1.101
集群已经搭建完毕,部署方法见NGINX+JDK+TOCAT配置。
二:安装和配置msm(memcached-session-manager)
1、安装memcached
yum install memcached -y
安装成功
注:可在两台机上面都安装memcached,memcached可以使用 no-
sticky session 和 sticky session 两种方式
这种使用
sticky session,需要配置失效节点。
no-sticky session,不需要配置是失效节点。
2、序列化
也就是说使用tomcat调用memcached来存储ses
sion需要使用到jar包。这些不同jar包的集合使用,就称为序列化(自己理解的) 。
让tomcat调用memcached来存储ses
sion早就是一个很成熟的解决方案了,开源的msm就可以解决这个问题。比较折腾的就是要用到的jar包,官方文档说的也比较含糊,我这里用的是kryo的序列化方案,所以用到的包多一些,分别是:
参考这个里面默认的jar包,不一定是最新的 。
jar放的目录也可以全部放在tomcat的家目录下面的lib下,例:/usr/local/tomcat-mbr/lib下
以上图中的jar包是我自己使用过的,并且成功了
3、tomcat的配置修改(是在no-sticky session方式下
)
1) 接下来是修改tomcat的配置文件$CATALINA_HOME/conf/context.xml,调整成新的session存储方式。配置文件中加入以下内容:
- <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
- memcachedNodes="n1:192.168.1.100:11211"
- sticky="false" //这是表示配置为 no-sticky session
- lockingMode="auto"
- sessionBackupAsync="false"
- sessionBackupTimeout="100" //sessionBackupTimeout的单位为分钟
- transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
- />
注:这种配置方式是只有安装一个memcached,这种配置试过是成功的。
2) 修改$CATALINA_HOME/conf/logging.properties文件,在logging.properties文件中的最后一行添加de.javakaffee.web.msm.level=FINE。
注:这个配置可以在catalina.out的日志中看到详细的session存取情况。
3)双节点的memcached的配置如下:
<Context>
...
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:192.168.1.100:11211,n2:192.168.1.101:11211"
sticky="false"
sessionBackupAsync="false"
lockingMode="uriPattern:/path1|/path2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
//这个选项是设置访问这一类.*\.(ico|png|gif|jpg|css|js)$的session不做保存,
sessionBackupTimeout="100"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
</Context>
4、先启动nginx,然后启动tomcat后,在启动memcached。
三:验证方法
验证方法如下:
1、192.168.1.101 部署的项目下放入
- [root@localhost lib]# vi /home/app/mbr/test.jsp /输入如下内容
- SessionID:<%=session.getId()%>
- <BR>
- SessionIP:<%=request.getServerName()%>
- <BR>
- SessionPort:<%=request.getServerPort()%>
- <%
- out.println("This is Tomcat Server 111111!");
- %>
2、192.168.1.100 部署的项目下放入
- [root@localhost lib]#vi /home/app/mbr/test.jsp /输入如下内容
- SessionID:<%=session.getId()%>
- <BR>
- SessionIP:<%=request.getServerName()%>
- <BR>
- SessionPort:<%=request.getServerPort()%>
- <%
- out.println("This is Tomcat Server 222222!");
- %>
通过浏览器访问Nginx服务器,如下图将访问地址改成Nginx所在服务器。
反复刷新浏览器,如果SessionID一直不变,下面的SessionPort的内容在不断变化则说明配置成功。
四:测试
1、在memcached使用 no-sticky session 下,只在192.168.1.100安装了memcached
配置如下:
- <ManagerclassName="de.javakaffee.web.msm.MemcachedBackupSessionManager" //这个是必须的选项
- memcachedNodes="n1:192.168.1.100:11211" //这个是必须的选项
- sticky="false"//这是表示配置为 no-sticky session
- lockingMode="auto"
- sessionBackupAsync="false"
- //只有在这个设置为false,sessionBackupTimeout这个才会生效。如果设置为true的话,backupThreadCount设置生效
- sessionBackupTimeout="100
- //sessionBackupTimeout的单位为毫秒。这个值是设置超时后在几毫秒内会话备份被认为是失败的还是现在就失效 ,默认100ms。
- transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
- />
测试验证:
[root@uc-proxy-01 ~]# ab -c 20 -n 1000 http://61.140.20.194:8101/test1.jsp
Requests per second: 164.19 [#/sec] (mean)
Time per request: 121.808 [ms] (mean)
Time per request: 6.090 [ms] (mean, across all concurrent requests)
Transfer rate: 58.53 [Kbytes/sec] received
2、一个
memcached,sticky session
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:192.168.1.100:11211"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
测试验证:
[root@uc-proxy-01 conf]# ab -c 20 -n 1000 http://61.140.20.194:8101/test1.jsp
Requests per second: 409.07 [#/sec] (mean)
Time per request: 48.891 [ms] (mean)
Time per request: 2.445 [ms] (mean, across all concurrent requests)
Transfer rate: 145.81 [Kbytes/sec] received
总结 验证结果:在1,2的配置下,
sticky session 的配置处理能力更强,经过查看日志,(发现储存在memcached中的session,会定时10s去检查,超过一段时间后 就会自动删除保存的session ID ,这种是在
sticky session 下才会开始检查session过期时间,然后打印出日志。
规律就是都在1800-1790之内开始检查session,每隔半小时开始Updating expiration time for session,然后到他的session有效保存期就直接删除。
3、
在memcached使用sticky session 下,安装了两个memcached
注:下面是第一台的tomcat的配置
</Context>
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:192.168.1.100:11211,n2:192.168.1.101:11211"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
Requests per second: 696.17 [#/sec] (mean)
ab -c 1000 -n 1500 http://61.140.20.194:8101/test1.jsp
Requests per second: 581.66 [#/sec] (mean)
4、在memcached使用no-sticky session 下,安装了两个memcached
</Context>
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:192.168.1.100:11211,n2:192.168.1.101:11211"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
Requests per second: 604.28 [#/sec] (mean) | Requests per second: 676.97 [#/sec] (mean)
ab -c 1000 -n 1500 http://61.140.20.194:8101/test1.jsp
Requests per second: 506.50 [#/sec] (mean)
Requests per second: 404.23 [#/sec] (mean)
Requests per second: 645.04 [#/sec] (mean)
Requests per second: 417.81 [#/sec] (mean)
测试结果:如果并发数不高的话例如100,sticky 和no-sticky的处理能力都没有多大的差别
5. 测试过程中遇到的问题
问题1: 已解决
为什么有时候日志中会存在两个session Id ,
没有说明节点的时候,这时候是单个的tomcat保存的sessionID,所以有两个,日志中也可以查询的到这个sessionID不生效,因为没有节点。
这时候,刷新页面只会获取到一个,并且一直是这个。这个是因为已经获取到了一个有效的sessionID。
问题2: 已解决
session ID保存的最长时间在哪里设置?
这个项目里面会自己设置的,默认的时间是30分钟。
问题3:
如果使用sticky session ,会打印日志检测session的有效时间,这个打印日志比较多。
如果使用no-sticky session ,这个则不会打印检测session的有效时间的日志
解决方法:定时清理保存的日志
6 memcached的启动参数优化
修改memcached的启动参数
需要在vi /etc/sysconfig/memcached修改
PORT="11211"
USER="memcached"
MAXCONN="200" //本来是1024,修改为200
CACHESIZE="64"
OPTIONS=""
还需要修改vi /etc/init.d/memcached
PORT=11211
USER=memcached
MAXCONN=200
CACHESIZE=64
OPTIONS="
重新启动service memcached restart