注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

小葫芦君(汉斯的博客)

博客迁移到新博客:https://blog.ssxingshou.com

 
 
 

日志

 
 
关于我

小小葫芦商城,为您提供高品质的商品,一流的产品,一流的包装服务,一流的物流服务,放心购买

网易考拉推荐

Tomcat(6.0.14) Session创建机制简介  

2010-12-01 10:53:07|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
背景:
公司的一个web应用,提交给测试部门做压力测试(由于不是我负责的,不清楚如何做的压力测试,以及测试指标),结果没压多久,就出现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方案都能有效解决文中所描述的问题.
  评论这张
 
阅读(1904)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017