Tomcat6 Session建立機制簡介

來源:互聯網
上載者:User
背景:
  測試部門做壓力測試, 結果沒壓多久,就出現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方案都能有效解決文中所描述的問題.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.