引用
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; ifnull
, 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 } }