资讯专栏INFORMATION COLUMN

Tomcat的session管理探究

rollback / 1284人阅读

摘要:下面是源代码片段的调用链是这样的里有一个线程实例,这个线程会每隔的时间调用,这个方法调用,这个方法调用,这个方法调用,在这里清理掉已经过期的。下面是源代码片段的值会作为新的默认的值实际上用户在到后修改这个值。

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

先说说这几个关键类:

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

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

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的调用链是这样的:

ContainerBase里有一个ContainerBackgroundProcessor线程实例,
这个线程会每隔ContainerBase.backgroundProcessorDelay的时间调用-->

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

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

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

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判断)

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

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

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

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

如果为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()方法时,发生了这些事情:

Request.doGetSession()

StandardSession.access()

返回给用户Session

用户在Servlet里处理完请求

Request.recycle()

StandardSession.endAccess()

陷阱

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

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

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64926.html

相关文章

  • Nginx+Tomcat关于Session管理

    摘要:前言对的管理一直有了解,但是一直没有实际操作一遍,本文从最简单的安装启动开始,通过实例的方式循序渐进的介绍了几种管理的方式。 前言 Nginx+Tomcat对Session的管理一直有了解,但是一直没有实际操作一遍,本文从最简单的安装启动开始,通过实例的方式循序渐进的介绍了几种管理session的方式。 nginx安装配置 1.安装nginx [root@localhost ~]# y...

    superw 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<