Tomcat中的session是如何管理的?

阅读更多
最近有个朋友问我:
引用
tomcat的session存在哪里?

答:内存中。又问:
引用
那既然都保存在内存中,要清除过期的sesion时,又怎么能区分是哪个应用的session而不会清除错误?

答:(当时没看过这块的代码),说由于每个应用对应一个Session管理,此处应该有类似于数组之类的东西,将该应用的session管理起来,当该应用移除或者某些sesion过期时可以准确的删除掉。最近看了下代码,对比下源码,发现回复的还行,没有特别大的出入。

我们的应用中一般都会用到session,那这个session在应用服务器内部到底是怎么保存到处理的呢?

当我们请求一个应用时,如果页面中用到了session,此时的创建session的调用链如下:

Daemon Thread [http-bio-8090-exec-1] (Suspended (breakpoint at line 145 in SessionIdGenerator))	
	owns: SocketWrapper  (id=234)	
	SessionIdGenerator.generateSessionId() line: 145	
	StandardManager(ManagerBase).generateSessionId() line: 807	
	StandardManager(ManagerBase).createSession(String) line: 653	
	Request.doGetSession(boolean) line: 2892	
	Request.getSession(boolean) line: 2315	
	RequestFacade.getSession(boolean) line: 898	
	RequestFacade.getSession() line: 910	
	PageContextImpl._initialize(Servlet, ServletRequest, ServletResponse, String, boolean, int, boolean) line: 146	
	PageContextImpl.initialize(Servlet, ServletRequest, ServletResponse, String, boolean, int, boolean) line: 125	
	JspFactoryImpl.internalGetPageContext(Servlet, ServletRequest, ServletResponse, String, boolean, int, boolean) line: 112	
	JspFactoryImpl.getPageContext(Servlet, ServletRequest, ServletResponse, String, boolean, int, boolean) line: 65	
	index.jsp line: not available	
	index_jsp(HttpJspBase).service(HttpServletRequest, HttpServletResponse) line: 70	
	index_jsp(HttpServlet).service(ServletRequest, ServletResponse) line: 728	
	JspServletWrapper.service(HttpServletRequest, HttpServletResponse, boolean) line: 432	
	JspServlet.serviceJspFile(HttpServletRequest, HttpServletResponse, String, boolean) line: 390	
	JspServlet.service(HttpServletRequest, HttpServletResponse) line: 334	
	JspServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 728	
	ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 305	
	ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210	
	StandardWrapperValve.invoke(Request, Response) line: 222	
	StandardContextValve.invoke(Request, Response) line: 123	
	NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 472	
	StandardHostValve.invoke(Request, Response) line: 171	
	ErrorReportValve.invoke(Request, Response) line: 99	
	AccessLogValve.invoke(Request, Response) line: 936	
	StandardEngineValve.invoke(Request, Response) line: 118	
	CoyoteAdapter.service(Request, Response) line: 408	
	Http11Processor(AbstractHttp11Processor).process(SocketWrapper) line: 1009	
	Http11Protocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler).process(SocketWrapper, SocketStatus) line: 589	
	JIoEndpoint$SocketProcessor.run() line: 310	
	ThreadPoolExecutor$Worker.runTask(Runnable) line: 895	
	ThreadPoolExecutor$Worker.run() line: 918	
	TaskThread(Thread).run() line: 662


以上调用链的部分关键代码:

	
	 /**
     * Construct and return a new session object, based on the default
     * settings specified by this Manager's properties.  The session
     * id specified will be used as the session id.  
     * If a new session cannot be created for any reason, return 
     * null.
     * 
     * @param sessionId The session id which should be used to create the
     *  new session; if null, a new session id will be
     *  generated
     * @exception IllegalStateException if a new session cannot be
     *  instantiated for any reason
     */
    @Override
    public Session createSession(String sessionId) {
        
        if ((maxActiveSessions >= 0) &&
                (getActiveSessions() >= maxActiveSessions)) {
            rejectedSessions++;
            throw new TooManyActiveSessionsException(
                    sm.getString("managerBase.createSession.ise"),
                    maxActiveSessions);
        }
        
        // Recycle or create a Session instance
        Session session = createEmptySession();

        // Initialize the properties of the new session and return it
        session.setNew(true);
        session.setValid(true);
        session.setCreationTime(System.currentTimeMillis());
        session.setMaxInactiveInterval(this.maxInactiveInterval);
        String id = sessionId;
        if (id == null) {
            id = generateSessionId();  //此处生成sessionID
        }
        session.setId(id);
        sessionCounter++;

        SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
        synchronized (sessionCreationTiming) {
            sessionCreationTiming.add(timing);
            sessionCreationTiming.poll();
        }
        return (session);

    }
	
	
	
	    public void setId(String id, boolean notify) {

        if ((this.id != null) && (manager != null))
            manager.remove(this);

        this.id = id;

        if (manager != null)
            manager.add(this); //注意此处

        if (notify) {
            tellNew();
        }
    }


在setId时,同时在Manager中将相应的session保存了下来。此处对于session的保存使用的是ConcurrentHashMap,在创建后将其添加到map中,session过期后将其移除。


    /**
     * Add this Session to the set of active Sessions for this Manager.
     *
     * @param session Session to be added
     */
    @Override
    public void add(Session session) {

        sessions.put(session.getIdInternal(), session);
        int size = getActiveSessions();  //此处应该可以使用AtomicInteger替换掉。
        if( size > maxActive ) {
            synchronized(maxActiveUpdateLock) {
                if( size > maxActive ) {
                    maxActive = size;
                }
            }
        }
    }	




而在停止一个应用时,相应的remove session的调用链如下:
	Daemon Thread [http-bio-8090-exec-2] (Suspended (breakpoint at line 733 in ManagerBase))	
	owns: StandardSession  (id=340)	
	owns: StandardManager  (id=326)	
	owns: StandardContext  (id=327)	
	owns: SocketWrapper  (id=341)	
	StandardManager(ManagerBase).remove(Session, boolean) line: 733	
	StandardSession.expire(boolean) line: 840	
	StandardManager.doUnload() line: 463	
	StandardManager.unload() line: 353	
	StandardManager.stopInternal() line: 518	
	StandardManager(LifecycleBase).stop() line: 232	
	StandardContext.stopInternal() line: 5569	
	StandardContext(LifecycleBase).stop() line: 232	
	HTMLManagerServlet(ManagerServlet).stop(PrintWriter, ContextName, StringManager) line: 1306	
	HTMLManagerServlet.stop(ContextName, StringManager) line: 733	
	HTMLManagerServlet.doPost(HttpServletRequest, HttpServletResponse) line: 221	
	HTMLManagerServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 647	
	HTMLManagerServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 728	
	ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 305	
	ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210	
	CsrfPreventionFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 213	
	ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243	
	ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210	
	SetCharacterEncodingFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 108	
	ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243	
	ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210	
	StandardWrapperValve.invoke(Request, Response) line: 222	
	StandardContextValve.invoke(Request, Response) line: 123	
	BasicAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 581	
	StandardHostValve.invoke(Request, Response) line: 171	
	ErrorReportValve.invoke(Request, Response) line: 99	
	AccessLogValve.invoke(Request, Response) line: 936	
	StandardEngineValve.invoke(Request, Response) line: 118	
	CoyoteAdapter.service(Request, Response) line: 408	
	Http11Processor(AbstractHttp11Processor).process(SocketWrapper) line: 1009	
	Http11Protocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler).process(SocketWrapper, SocketStatus) line: 589	
	JIoEndpoint$SocketProcessor.run() line: 310	
	ThreadPoolExecutor$Worker.runTask(Runnable) line: 895	
	ThreadPoolExecutor$Worker.run() line: 918	
	TaskThread(Thread).run() line: 662	



其中执行remove的代码如下:
	 /**
     * Perform the internal processing required to invalidate this session,
     * without triggering an exception if the session has already expired.
     *
     * @param notify Should we notify listeners about the demise of
     *  this session?
     */
    public void expire(boolean notify) {

        // Check to see if expire is in progress or has previously been called
        if (expiring || !isValid)
            return;

        synchronized (this) {
            // Check again, now we are inside the sync so this code only runs once
            // Double check locking - expiring and isValid need to be volatile
            if (expiring || !isValid)
                return;

            if (manager == null)
                return;
			... //省略部分代码
		}
		... //省略部分代码
			   if (ACTIVITY_CHECK) {
                accessCount.set(0);
            }
            setValid(false);

            // Remove this session from our manager's active sessions
            manager.remove(this, true);
	}

StandardManager中的remove方法:
    /**
     * Remove this Session from the active Sessions for this Manager.
     *
     * @param session   Session to be removed
     * @param update    Should the expiration statistics be updated
     */
    @Override
    public void remove(Session session, boolean update) {
        
        // If the session has expired - as opposed to just being removed from
        // the manager because it is being persisted - update the expired stats
        if (update) {
            long timeNow = System.currentTimeMillis();
            int timeAlive =
                (int) (timeNow - session.getCreationTimeInternal())/1000;
            updateSessionMaxAliveTime(timeAlive);
            expiredSessions.incrementAndGet();
            SessionTiming timing = new SessionTiming(timeNow, timeAlive);
            synchronized (sessionExpirationTiming) {
                sessionExpirationTiming.add(timing);
                sessionExpirationTiming.poll();
            }
        }

        if (session.getIdInternal() != null) {
            sessions.remove(session.getIdInternal());  //注意此处,即为上面保存session的concurrentHashMap
        }
    }

你可能感兴趣的:(tomcat,tomcat,session)