Tomcat6 Session创建机制简介

背景:
  测试部门做压力测试, 结果没压多久,就出现OutOfMemory.
 原因查找,通过监控工具,发现 StandardSession(org.apache.catalina.session. StandardSession)对象不断增长,毫无疑问,肯定是在不断创建Session对象.
备注: 一般做压力测试,每次请求都不会指定JESSESIONID值,导致Web容器认为每次请求都是新的请求,于是创建Session对象.
同事负责代码Review,发现应用没有任何一个地方存放Session内容.困惑之...

问题:Tomcat容器何时创建Session对象?
想当然认为,只有动态存放Session内容的时候,才会创建Session对象.但是事实真得如此吗?

先看Servlet协议描述:
请看:
getSession( boolean  create)方法:
javax.servlet.http.HttpServletRequest.getSession( boolean  create)

Returns the current HttpSession associated with this request or, if if there is no current session and create is true, returns a new session. 

If create is false and the request has no valid HttpSession, this method returns null. 

To make sure the session is properly maintained, you must call this method before the response is committed.

简单地说:当create变量为true时,如果当前Session不存在,创建一个新的Session并且返回.

getSession()方法:
javax.servlet.http. HttpSession getSession();

Returns the current session associated with this request, or if the request does not have a session, creates one.
简单的说:当当前Session不存在,创建并且返回.


所以说,协议规定,在调用getSession方法的时候,就会创建Session对象.



既然协议这么定了,我们再来看看Tomcat是如何实现的:(下面的描述,是基于Tomcat6.0.14版本源码)
先看一张简单的类图:


ApplicationContext:Servlet规范中ServletContext的实现
StandardContext:Tomcat定义的Context默认实现.维护了一份SessionManager对象,管理Session对象.所有的Session对象都存放在Manager定义的Map<String,Session>容器中.
StanardManager:标准的Session管理,将Session存放在内容,Web容器关闭的时候,持久化到本地文件
PersistentManager:持久化实现的Session管理,默认有两种实现方式:
--持久化到本地文件
--持久化到数据库

了解了大概的概念后,回头再来看看org.apache.catalina.connector. Request.getSession()是如何实现的.
最终调用的是doGetSession(boolean create)方法,请看:
protected  Session doGetSession( boolean  create) {

        
//  There cannot be a session if no context has been assigned yet
         if  (context  ==   null )
            
return  ( null );

        
//  Return the current session if it exists and is valid
         if  ((session  !=   null &&   ! session.isValid())
            session 
=   null ;
        
if  (session  !=   null )
            
return  (session);

        
//  Return the requested session if it exists and is valid
        Manager manager  =   null ;
        
if  (context  !=   null )
            manager 
=  context.getManager();
        
if  (manager  ==   null )
            
return  ( null );       //  Sessions are not supported
         if  (requestedSessionId  !=   null ) {
            
try  {
                session 
=  manager.findSession(requestedSessionId);
            } 
catch  (IOException e) {
                session 
=   null ;
            }
            
if  ((session  !=   null &&   ! session.isValid())
                session 
=   null ;
            
if  (session  !=   null ) {
                session.access();
                
return  (session);
            }
        }

        
//  Create a new session if requested and the response is not committed
         if  ( ! create)
            
return  ( null );
        
if  ((context  !=   null &&  (response  !=   null &&
            context.getCookies() 
&&
            response.getResponse().isCommitted()) {
            
throw   new  IllegalStateException
              (sm.getString(
" coyoteRequest.sessionCreateCommitted " ));
        }

        
//  Attempt to reuse session id if one was submitted in a cookie
        
//  Do not reuse the session id if it is from a URL, to prevent possible
        
//  phishing attacks
         if  (connector.getEmptySessionPath() 
                
&&  isRequestedSessionIdFromCookie()) {
            session 
=  manager.createSession(getRequestedSessionId());
        } 
else  {
            session 
=  manager.createSession( null );
        }

        
//  Creating a new session cookie based on that session
         if  ((session  !=   null &&  (getContext()  !=   null )
               
&&  getContext().getCookies()) {
            Cookie cookie 
=   new  Cookie(Globals.SESSION_COOKIE_NAME,
                                       session.getIdInternal());
            configureSessionCookie(cookie);
            response.addCookieInternal(cookie, context.getUseHttpOnly());
        }

        
if  (session  !=   null ) {
            session.access();
            
return  (session);
        } 
else  {
            
return  ( null );
        }

    }


至此,简单地描述了Tomcat Session创建的机制,有兴趣的同学要深入了解,不妨看看Tomcat源码实现.



补充说明,顺便提一下Session的过期策略.
过期方法在:
org.apache.catalina.session. ManagerBase(StandardManager基类) processExpires方法:
public   void  processExpires() {

        
long  timeNow  =  System.currentTimeMillis();
        Session sessions[] 
=  findSessions();
        
int  expireHere  =   0  ;
        
        
if (log.isDebugEnabled())
            log.debug(
" Start expire sessions  "   +  getName()  +   "  at  "   +  timeNow  +   "  sessioncount  "   +  sessions.length);
        
for  ( int  i  =   0 ; i  <  sessions.length; i ++ ) {
            
if  (sessions[i] != null   &&   ! sessions[i].isValid()) {
                expireHere
++ ;
            }
        }
        
long  timeEnd  =  System.currentTimeMillis();
        
if (log.isDebugEnabled())
             log.debug(
" End expire sessions  "   +  getName()  +   "  processingTime  "   +  (timeEnd  -  timeNow)  +   "  expired sessions:  "   +  expireHere);
        processingTime 
+=  ( timeEnd  -  timeNow );

    }

其中,Session.isValid()方法会做Session的清除工作.


在org.apache.catalina.core.ContainerBase中,会启动一个后台线程,跑一些后台任务,Session过期任务是其中之一:
protected   void  threadStart() {

        
if  (thread  !=   null )
            
return ;
        
if  (backgroundProcessorDelay  <=   0 )
            
return ;

        threadDone 
=   false ;
        String threadName 
=   " ContainerBackgroundProcessor[ "   +  toString()  +   " ] " ;
        thread 
=   new  Thread( new  ContainerBackgroundProcessor(), threadName);
        thread.setDaemon(
true );
        thread.start();

    }

准确地讲,除非你的应用完全不需要保存状态(无状态应用),不然地话,只要有一个新的连接过来,web容器都需要创建Session概念,维护状态信息.
但是Session是什么?Session仅仅是一个概念:"Provides a way to identify a user across more than one page request or visit to a Web site and to store information about that user."--简单地讲,保存用户状态信息.
所以说,我们完全可以根据应用的需求,定制Session的实现:
a. Session保存到JVM内容中--Tomcat默认的实现
b. Session保存到Cookie中--Cookie-Based Session
c. Session保存到本地文件--Tomcat提供的非默认实现之一
d. Session保存到Cache Store中--比如常见的Memcached
e. Session保存到数据库中--比如保存到mysql数据库session表,中间对于活跃的Session 缓存到cached中.
......
那么,假如一个应用有大量一次性不同用户的请求(仅仅是一次性的,比如上述文章描述的场景),那么选择c,d,e方案都能有效解决文中所描述的问题.
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值