浅谈Tomcat Session管理分析

2019-10-18 19:52:32刘景俊

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(((Context) getContainer()).getSessionTimeout() * 60);
  String id = sessionId;
  if (id == null) {
    id = generateSessionId();
  }
  session.setId(id);
  sessionCounter++;
 
  SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
  synchronized (sessionCreationTiming) {
    sessionCreationTiming.add(timing);
    sessionCreationTiming.poll();
  }
  return (session);
 
}

如果传的sessionId为空,tomcat会生成一个唯一的sessionId,具体可以参考类StandardSessionIdGenerator的generateSessionId方法;这里发现创建完session之后并没有把session放入ConcurrentHashMap中,其实在session.setId(id)中处理了,具体代码如下:

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();
  }
}

4.销毁Session

Tomcat会定期检测出不活跃的session,然后将其删除,一方面session占用内存,另一方面是安全性的考虑;启动tomcat的同时会启动一个后台线程用来检测过期的session,具体可以查看ContainerBase的内部类ContainerBackgroundProcessor:

protected class ContainerBackgroundProcessor implements Runnable {
 
   @Override
   public void run() {
     Throwable t = null;
     String unexpectedDeathMessage = sm.getString(
         "containerBase.backgroundProcess.unexpectedThreadDeath",
         Thread.currentThread().getName());
     try {
       while (!threadDone) {
         try {
           Thread.sleep(backgroundProcessorDelay * 1000L);
         } catch (InterruptedException e) {
           // Ignore
         }
         if (!threadDone) {
           Container parent = (Container) getMappingObject();
           ClassLoader cl =
             Thread.currentThread().getContextClassLoader();
           if (parent.getLoader() != null) {
             cl = parent.getLoader().getClassLoader();
           }
           processChildren(parent, cl);
         }
       }
     } catch (RuntimeException e) {
       t = e;
       throw e;
     } catch (Error e) {
       t = e;
       throw e;
     } finally {
       if (!threadDone) {
         log.error(unexpectedDeathMessage, t);
       }
     }
   }
 
   protected void processChildren(Container container, ClassLoader cl) {
     try {
       if (container.getLoader() != null) {
         Thread.currentThread().setContextClassLoader
           (container.getLoader().getClassLoader());
       }
       container.backgroundProcess();
     } catch (Throwable t) {
       ExceptionUtils.handleThrowable(t);
       log.error("Exception invoking periodic operation: ", t);
     } finally {
       Thread.currentThread().setContextClassLoader(cl);
     }
     Container[] children = container.findChildren();
     for (int i = 0; i < children.length; i++) {
       if (children[i].getBackgroundProcessorDelay() <= 0) {
         processChildren(children[i], cl);
       }
     }
   }
 }