下面谈一谈Session的管理。
一. Session的管理
当tomcat启动时,会开启一个后台线程,这个后台线程是随容器的启动而启动的,它将定期检查会话超时。
(org.apache.catalina.core.ContainerBase.threadStart())
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(); }
1.此时会新建一个ContainerBackgroundProcessor线程。
(org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor)
protected class ContainerBackgroundProcessor implements Runnable { public void run() { while (!threadDone) { try { Thread.sleep(backgroundProcessorDelay * 1000L); } catch (InterruptedException e) { ; } if (!threadDone) { Container parent = (Container) getMappingObject(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (parent.getLoader() != null) { cl = parent.getLoader().getClassLoader(); } processChildren(parent, cl);//parent为StandardEngine } } }
2.processChildren(parent,cl)--->processChildren(StandardHost,cl) --->processChildren(StandardContext,cl),最后StandardContext#backgroundProcess()方法被调用。
(org.apache.catalina.core.StandardContext.backgroundProcess()) public void backgroundProcess() { if (!started) return; count = (count + 1) % managerChecksFrequency; if ((getManager() != null) && (count == 0)) { try { getManager().backgroundProcess(); } catch ( Exception x ) { log.warn("Unable to perform background process on manager",x); } }
3. StandardManager#processExpires()方法被调用。
org.apache.catalina.session.StandardManager.backgroundProcess()
public void backgroundProcess() { processExpires(); }
4. StandardManager#processExpires()被调用,通过调用session#isValid()方法判断session是否有效,如果session无效将被销毁。
(org.apache.catalina.session.StandardManager.processExpires())
/**
* 处理所有已失效的session
*/
public void processExpires() { long timeNow = System.currentTimeMillis(); Session sessions[] = findSessions(); for (int i = 0; i < sessions.length; i++) { StandardSession session = (StandardSession) sessions[i]; if (!session.isValid()) { expiredSessions++; } } long timeEnd = System.currentTimeMillis(); processingTime += ( timeEnd - timeNow ); }
5.StandardSession#isValid()最终调用expire(true),然后返回isValid()的布尔值。
(org.apache.catalina.session.StandardSession.isValid())
public boolean isValid() { if (this.expiring) { return true; } if (!this.isValid ) { return false; } if (accessCount > 0) { return true; } if (maxInactiveInterval >= 0) { long timeNow = System.currentTimeMillis(); int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L); if (timeIdle >= maxInactiveInterval) { expire(true);※3 } } return (this.isValid); }
※3
StandardSession.expire()方法被调用,session被销毁。
(org.apache.catalina.session.StandardSession.expire(boolean notify))
public void expire(boolean notify) { if (expiring) return; synchronized (this) { if (manager == null) return; expiring = true; //唤醒相关的应用程序事件监听者 Context context = (Context) manager.getContainer(); Object listeners[] = context.getApplicationLifecycleListeners(); if (notify && (listeners != null)) { HttpSessionEvent event = new HttpSessionEvent(getSession()); for (int i = 0; i < listeners.length; i++) { int j = (listeners.length - 1) - i; if (!(listeners[j] instanceof HttpSessionListener)) continue; HttpSessionListener listener = (HttpSessionListener) listeners[j]; try { fireContainerEvent(context, "beforeSessionDestroyed", listener); listener.sessionDestroyed(event); fireContainerEvent(context, "afterSessionDestroyed", listener); } catch (Throwable t) { try { fireContainerEvent(context, "afterSessionDestroyed", listener); } catch (Exception e) { ; } manager.getContainer().getLogger().error (sm.getString("standardSession.sessionEvent"), t); } } } accessCount = 0; setValid(false); // Remove this session from our manager's active sessions if (manager != null) manager.remove(this); //唤醒相关的session事件监听者 if (notify) { fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null); } // 处理完毕后,设置expiring为false expiring = false; //解除任何与此相关的session对象的绑定 String keys[] = keys(); for (int i = 0; i < keys.length; i++) removeAttributeInternal(keys[i], notify); } }