看到有留言,对我如此“执着”的关注session创建很好奇,解释一下吧。
首先是关注性能,前面提到过session的使用是有代价的,需要在保存在服务器端内容中,每次request.getSeesion()方法获取session时,实际是在服务器段的一个大的hasp结构中以当前的jsessionid为key,获取对应的value HttpSession对象,这个过程是需要消耗cpu的,当然目前hash算法比较好,这里消耗不那么明显。而一般的应用,消耗的cpu远比这个小开销大出2-3个数量级,因此通常情况不敏感。如果这个session是我们需要使用的,那么付出这些内存和cpu的代码是完全值得的。但是,如果产生大量的没有任何用处的"垃圾session",对大容量,大并发,需要长期稳定运行的系统会带来很无谓的负载。
注意,我们要讨论是"垃圾session",即是在我们计划外因为某个原因创建,从不使用,完全浪费的session。正常使用的session不在讨论范围内,虽然也有些比较极端的系统号称不使用session来提高服务器性能,有些对性能比较关注的系统/框架则采用其他的方式来避免使用session,有兴趣的可以google找资料看。
下面我们来进行一个简单的性能测试,模拟一下比较极端的情况,我在linux下启动resin,只跑两个最简单的jsp文件:
a.jsp不会自动生成session:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page session="false" %>
<!DOCTYPE html PUBLIC "-//W3C/m/DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<%=1%>
</body>
</html>
b.jsp基本相同,但是设置为<%@ page session="true" %>,这个将自动增加
HttpSession session = request.getSesson(true)
语句,我们进行最恶劣的假设,每次请求都生成新的session,看看会是什么情况:
测试工具采用loadrunner,部署在我的笔记本上(dell d620机器,intel 双核 2g内存)。测试比较简单,测试方法和过程忽略(loadrunner的使用也不复杂),只给出结果:
测试中两个场景的运行情况相同:100个线程并发,每次只访问一下a.jsp/b.jsp,运行时间2分钟。使用top命令在服务器段看resin的内存消耗和cpu使用情况,这个只能大概估计,不好准确衡量。
补充:第一次测试时忘了设置resin的<session-max>参数了,后来写了一个sessionLinstener做计数器,resin的默认没有配置session-max,这个时候resin取的值是默认4096,计数器打印日志发现session总数达到4096后,就会remove掉已经存在的session以保证不超过4096.下面的测试数据时将session-max设置为4096000后测试的结果
测试1:
a.jsp 不生成session
resin内存消耗始终是在175-177之间,基本没有变化。cpu基本稳定在19%-22%。
测试2:
b.jsp 每次生成session
resin内存消耗从181m逐步增加,到2分钟测试结束时达到283m,大概增加了102M。cpu大体在25%-30%间。
下面可以总结了:
1. 垃圾session会占用内存
上面只测试了2分钟,考虑通常session的超时时间会是30分钟,这意味着这些占据的内存至少要到30分钟之后resin才能判定超时从resin的session hash结构出移除,之后再被jvm回收。
创建的session总数为202941,102M内存/202941次请求=
527字节/session,这个数据比较粗糙,但是还是能大体反映问题。一个垃圾session大概要浪费我们0.5k的内存,时间长达30分钟。
2. 垃圾session的调用消耗了cpu资源
HttpSession session = request.getSesson(true),每次都要new出新的HttpSession对象,然后resin还要给这个HttpSession算出一个jsessionid,再将jsessionid/session以key/value保存到hash结构中。以后resin检查session超时的线程每次检查时都要查看每个垃圾session,看是否超时。
3. 垃圾session增加正常获取session的开销
前面提到垃圾session也是要保存hash结构中,request.getSesson()每次都要用jsessionid在这个hash结构中取一次数据。当垃圾session大量充斥时,获取当正常有用的session的时间也会增加,具体就要看这个hash结构的算法如何了。
当前上述的测试都是建立在最苛刻最恶劣的情况下,大多数情况我们的系统不会这么糟糕,也不是每个系统都对访问量/性能有高要求。如果觉得可以浪费的起,那浪费好了,只是我这边系统的情况不同,我们的产品对性能很敏感,能省就省点。
除考虑性能外, session的创建在我们新设计的系统中,是必须非常严格控制的.这个和我们目前的系统结构,包括部署/用户身份认证有关.简单的说我们的系统是基于apache + resin的多机分布, 各个功能模块是作为不同的webapp发布的,session的生成和jsessionid的传递必须可控,因此我必须严格掌控系统中session的生成情况。
另外说一点个人意见,知不知道有这些session自动创建的情况,和决定是否要在自己的代码中严格控制session创建,不是同一个概念。完全可以在了解情况后,根据自己的实际需要和个人习惯去做决定,比如选择无视。但是如果不知道呢?呵呵,很惭愧,在这次探索之前,我对jsp/webwork标签自动生成session是没有概念的,我们的原有系统是不使用HttpSession的(和当时的分布式方案有关),但是现在看来,由于jsp/webwork标签的存在,其实每次都有session被创建,这些session也就成了我上面说的垃圾session: 在不需要创建时意外创建,从来不使用,除了浪费资源外没有其他存在价值。
整理一下留言中的内容,感谢大家的关注:
疑问:
我觉得系统的瓶颈不会出在session生成上
,花时间在后台缓存和sql优化、架构调整这些事情上来的实惠.
回答:
滴水的水龙头要不要拧紧的问题?
没有哪个家庭的财政会因为那个滴水的水龙头造成收支失衡以致破产,那是否就意味着可以无视它的存在,只管专心挣钱,再每个月少吃一次大餐?后台缓存和sql优化、架构调整,这些当然是更重要的,但不意味着其他东西就可以完全忽略。我无意强调session的这些细节的高度,只是希望告诉大家,在某些角落里,有些我们没有意识到的小水龙头,在一滴一滴的浪费资源。
后台缓存和sql优化、架构调整,到处可以找到资料。可是我google了一圈,上述session的几个问题根本没有成文的全面一点的资料可以参考,我相信jsp世界中肯定还有很多很多类似的小水龙头在悄悄地滴水,而主人们根本没有看见。