Tomcat的session管理探究

我有一个项目需要模拟HttpSession,在参考Tomcat的HttpSession管理时有一点心得,在这里记录一下。

先说说这几个关键类:

  1. org.apache.catalina.session.StandardManager: 管理Session的类

  2. org.apache.catalina.session.StandardSession: HttpSession的实现

  3. org.apache.catalina.connector.Request: HttpServletRequest的实现

StandardManager

下面介绍一下和Session相关的几个关键属性,以及方法

processExpiresFrequency

每隔多少次StandardManager.backgroundProcess做一次session清理,数字越小越频繁,默认6次。

下面是源代码片段:

/

  • Frequency of the session expiration, and related manager operations.
  • Manager operations will be done once for the specified amount of
  • backgrondProcess calls (ie, the lower the amount, the most often the
  • checks will occur).
    */
    protected int processExpiresFrequency = 6;
    StandardManager.backgroundProcess的调用链是这样的:
  1. ContainerBase里有一个ContainerBackgroundProcessor线程实例,
    这个线程会每隔ContainerBase.backgroundProcessorDelay的时间调用-->

  2. ContainerBase.processChildren,这个方法调用-->

  3. ContainerBase.backgroundProcess,这个方法调用-->

  4. StandardManager.backgroundProcess,这个方法调用-->

  5. StandardManager.processExpires,在这里清理掉已经过期的Session。

maxInactiveInterval

一个session不被访问的时间间隔,默认30分钟(1800秒)。

下面是源代码片段:

/

  • The default maximum inactive interval for Sessions created by
  • this Manager.
    /
    protected int maxInactiveInterval = 30
    60;
    StandardManager.maxInactiveInterval的值会作为新Session的默认maxInactiveInterval的值
    (实际上用户在get到session后修改这个值)。

下面是代码片段:

public Session createSession(String sessionId) {
// ...

// 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);

// ...
}

StandardSession

access()

StandardSession.access方法是用来设置这个Session被访问的时间的,何时被调用会在Request里讲。

下面是代码片段:

/

  • Update the accessed time information for this session. This method

  • should be called by the context when a request comes in for a particular

  • session, even if the application does not reference it.
    */
    @Override
    public void access() {

    this.thisAccessedTime = System.currentTimeMillis();

    if (ACTIVITY_CHECK) {
    accessCount.incrementAndGet();
    }

}

endAccess()

StandardSession.endAccess方法是用来设置这个Session访问结束的时间的,何时被调用会在Request里讲。

/

  • End the access.
    */
    @Override
    public void endAccess() {

    isNew = false;

    /

    • The servlet spec mandates to ignore request handling time
    • in lastAccessedTime.
      */
      if (LAST_ACCESS_AT_START) {
      this.lastAccessedTime = this.thisAccessedTime;
      this.thisAccessedTime = System.currentTimeMillis();
      } else {
      this.thisAccessedTime = System.currentTimeMillis();
      this.lastAccessedTime = this.thisAccessedTime;
      }

    if (ACTIVITY_CHECK) {
    accessCount.decrementAndGet();
    }

}

isValid()

StandardSession.isValid方法是很关键的,这个方法会用来判断这个Session是否还处于有效状态。

代码片段:

/

  • Return the isValid flag for this session.
    */
    @Override
    public boolean isValid() {

    if (!this.isValid) {
    return false;
    }

    if (this.expiring) {
    return true;
    }

    if (ACTIVITY_CHECK && accessCount.get() > 0) {
    return true;
    }

    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);
    }
    if (timeIdle >= maxInactiveInterval) {
    expire(true);
    }
    }

    return this.isValid;
    }
    ACTIVITY_CHECK,的意思是判断session是否过期前,是否要先判断一下这个session是否还在使用中(用accessCount判断)

  1. 如果是,那么这个session是不会过期的。

  2. 如果不是,那么这个session就会被“粗暴”地过期。

LAST_ACCESS_AT_START,是两种判断session过期方式的开关

  1. 如果为true,会根据getSession的时间判断是否过期了。access()和endAccess()之间的时间是不算进去的。

  2. 如果为false,则根据session结束访问的时间判断是否过期了。access()和endAccess()之间的时间是算进去的。

Request

doGetSession()

这个方法是tomcat获得session的地方,从下面的代码判断里可以看到,它会调用StandardSession.access()方法:

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) {
// 在这里调用了access
session.access();
return (session);
}
}
// ...
}

recycle()

这个当一个请求处理完毕后,CoyoteAdapter会调用Request.recycle()方法,
而这个方法会调用StandardSession.endAccess()方法(也就是告诉Session,你的这次访问结束了)

/

  • Release all object references, and initialize instance variables, in

  • preparation for reuse of this object.
    */
    public void recycle() {

    // ...
    if (session != null) {
    try {
    session.endAccess();
    } catch (Throwable t) {
    ExceptionUtils.handleThrowable(t);
    log.warn(sm.getString("coyoteRequest.sessionEndAccessFail"), t);
    }
    }
    // ...

}
所以,当用户调用HttpSession.getSession()方法时,发生了这些事情:

  1. Request.doGetSession()

  2. StandardSession.access()

  3. 返回给用户Session

  4. 用户在Servlet里处理完请求

  5. Request.recycle()

  6. StandardSession.endAccess()

陷阱

从上面的流程可以看出Tomcat假设在Request的生命周期结束之后便不会有人再去访问Session了。

但是如果我们在处理Request的Thread A里另起一个Thread B,并且在Thread B里访问Session时会怎样呢?

你可能已经猜到,可能会访问到一个已经过期的Session。下面是一个小小的测试代码:

https://gist.github.com/chanjarster/e1793251477cbabfbe92

关键字:tomcat, session, java


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部