Tomcat 源码分析 (Tomcat的Session管理之管理过期Session) (十二)

网友投稿 724 2022-08-30

Tomcat 源码分析 (Tomcat的Session管理之管理过期Session) (十二)

Tomcat 源码分析 (Tomcat的Session管理之管理过期Session) (十二)

文章目录

​​0.前记​​​​1.判断session是否有效​​​​2.处理Session过期​​

0.前记

在之前的文章中通过​​doGetSession()​​​获取​​Session​​

protected Session doGetSession(boolean create) { // There cannot be a session if no context has been assigned yet Context context = getContext(); if (context == null) { return (null); } //11 if ((session != null) && !session.isValid()) { session = null; } if (session != null) { return (session); } //省略}

1.判断session是否有效

判断Session 是否有效

/** * Return the isValid flag for this session. */@Overridepublic boolean isValid() { //如果是无效 返回无效 if (!this.isValid) { return false; } //如果已经过期了,就返回有效 if (this.expiring) { return true; } //该session的访问次数大于0 有效session if (ACTIVITY_CHECK && accessCount.get() > 0) { return true; } //如果 允许的最大闲置时间大于0,也就是存在最大闲置时间 if (maxInactiveInterval > 0) { //现在 long timeNow = System.currentTimeMillis(); //空闲时间 int timeIdle; if (LAST_ACCESS_AT_START) { timeIdle = (int) ((timeNow - lastAccessedTime) / 1000L); } else { timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L); } //如果空闲时间 大于允许的最大闲置时间,那么就将session置为过期 if (timeIdle >= maxInactiveInterval) { expire(true); } } return this.isValid;}

2.处理Session过期

session过期的话

public void expire(boolean notify) { //再次检查 isValid属性因为有可能已经被别的线程置为无效,也意味着该session已经被过期处理 if (!isValid) return; //锁定该session对象 synchronized (this) { //再次检查 if (expiring || !isValid) return; if (manager == null) return; //将expiring 设置为过期 expiring = true; // Notify interested application event listeners // FIXME - Assumes we call listeners in reverse order Context context = (Context) manager.getContainer(); ClassLoader oldTccl = null; if (context.getLoader() != null && context.getLoader().getClassLoader() != null) { oldTccl = Thread.currentThread().getContextClassLoader(); if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction pa = new PrivilegedSetTccl( context.getLoader().getClassLoader()); AccessController.doPrivileged(pa); } else { Thread.currentThread().setContextClassLoader( context.getLoader().getClassLoader()); } } try { //获取Context的所有的-,然后触发 beforeSessionDestroyed 和 afterSessionDestroyed事件 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 { context.fireContainerEvent("beforeSessionDestroyed", listener); listener.sessionDestroyed(event); context.fireContainerEvent("afterSessionDestroyed", listener); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); try { context.fireContainerEvent( "afterSessionDestroyed", listener); } catch (Exception e) { // Ignore } manager.getContainer().getLogger().error (sm.getString("standardSession.sessionEvent"), t); } } } } finally { if (oldTccl != null) { if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction pa = new PrivilegedSetTccl(oldTccl); AccessController.doPrivileged(pa); } else { Thread.currentThread().setContextClassLoader(oldTccl); } } } if (ACTIVITY_CHECK) { accessCount.set(0); } //触发SESSION_DESTROYED_EVENT事件,区别于上面是所触发的-不同 if (notify) { fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null); } // session注销,对应的用户登出 if (principal instanceof GenericPrincipal) { GenericPrincipal gp = (GenericPrincipal) principal; try { gp.logout(); } catch (Exception e) { manager.getContainer().getLogger().error( sm.getString("standardSession.logoutfail"), e); } } // 设置成无效的session setValid(false); expiring = false; String keys[] = keys(); if (oldTccl != null) { if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction pa = new PrivilegedSetTccl( context.getLoader().getClassLoader()); AccessController.doPrivileged(pa); } else { Thread.currentThread().setContextClassLoader( context.getLoader().getClassLoader()); } } try { //remove掉所有session内部的对象 for (int i = 0; i < keys.length; i++) { removeAttributeInternal(keys[i], notify); } } finally { if (oldTccl != null) { if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction pa = new PrivilegedSetTccl(oldTccl); AccessController.doPrivileged(pa); } else { Thread.currentThread().setContextClassLoader(oldTccl); } } } }}

代码内部一再检查​​isValid​​​属性, 理论上同一个用户只会同时开一个线程, 也就是同时只有一个线程更改​​session​​属性。

再次检查属性是为了防止其他线程修改, 因为​​Catalina​​​内部有一个定时线程不停的跑过期​​session​​​, 为了让很多不调用​​getSession()​​​的方法的​​session​​不销毁。

​​StandardManager.backgroundProcess()​​做一些周期性任务

@Override public void backgroundProcess() { count = (count + 1) % processExpiresFrequency; if (count == 0) processExpires(); }

处理expires()

​​ManagerBase.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++) { //调用isValid() 处理过期session 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 );}

不管是主动​​getSession()​​​还是后台周期任务处理,都会调用​​isValid()​​​方法处理过期​​session​​。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:转MYSQL学习(四) 查询
下一篇:图解kubernetes调度器framework核心数据结构·
相关文章

 发表评论

暂时没有评论,来抢沙发吧~