tomcat session管理

2/22/2017来源:ASP.NET技巧人气:1000

最近有空看了一下tomcat 6源码里面对session管理的实现,现在写下来,以供后考,也希望能对对此感兴趣的朋友

有所提示。

 

闲话少说,先贴一下tomcat6的component层次图(此图来自tomcat doc)

  Server                                         就是一个servlet container            |          Service                                 包含一个或多个connector的组                     |          Engine                                  servlet engine.最顶级的container.             |  \             |  --- Cluster --*            |          Host                                      第二级container            |          ------         /      \      Cluster    Context(1-N)             第三级container. servlet context. 也就是一个应用。         |             \         |             -- Manager               应用的session管理器         |                   \         |                   -- DeltaManager         |                   -- BackupManager         |      ---------------------------         |                       \       Channel                    \     ----------------------------- \         |                          \      Interceptor_1 ..               \         |                            \      Interceptor_N                    \     -----------------------------      \      |          |         |             \    Receiver    Sender   Membership       \                                          -- Valve                                          |      \                                          |       -- ReplicationValve                                          |       -- JvmRouteBinderValve                                           |                                          -- LifecycleListener                                           |                                          -- ClusterListener                                           |      \                                          |       -- ClusterSessionListener                                          |       -- JvmRouteSessionIDBinderListener                                          |                                          -- Deployer                                                  \                                                  -- FarmWarDeployer      

OK,基本层级关系说过了,就开始分析session的管理。

1. session 的创建。

   更正:下面这段我理解错了。其实进一步看下来,发现其实session的创建是在每个context里面。具体的创建可以看我

   这篇之后的那篇文章. Session的生成实际是在调用最终处理的servlet的时候生成的。

    session的创建的入口是在Host里面,每次request进来之后,会沿着container 的pipeline一直往下进行、

    处理,顺序是:engine->host->context->wrapper. 而pipeline的作用就是调用对应级别的value对

   request和response进行处理。在StandardHostValue里面首先出现对session的调用:

 

java代码  收藏代码 public final void invoke(Request request, Response response)           throws IOException, ServletException {              // Select the Context to be used for this Request           Context context = request.getContext();           if (context == null) {               response.sendError                   (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,                    sm.getString("standardHost.noContext"));               return;           }              // Bind the context CL to the current thread           if( context.getLoader() != null ) {               // Not started - it should check for availability first               // This should eventually move to Engine, it's generic.               Thread.currentThread().setContextClassLoader                       (context.getLoader().getClassLoader());           }              // Ask this Context to PRocess this request           context.getPipeline().getFirst().invoke(request, response);              // access a session (if present) to update last accessed time, based on a           // strict interpretation of the specification           if (Globals.STRICT_SERVLET_COMPLIANCE) {               request.getSession(false);           }              // Error page processing           response.setSuspended(false);              Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);              if (t != null) {               throwable(request, response, t);           } else {               status(request, response);           }              // Restore the context classloader           Thread.currentThread().setContextClassLoader               (StandardHostValve.class.getClassLoader());          }  

 

    注意里面的:request.getSession(false) 这一句。这一句最终会调用如下代码;

  

Java代码  收藏代码 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) {                  session.access();                  return (session);              }          }             // Create a new session if requested and the response is not committed          if (!create)              return (null);          if ((context != null) && (response != null) &&              context.getCookies() &&              response.getResponse().isCommitted()) {              throw new IllegalStateException                (sm.getString("coyoteRequest.sessionCreateCommitted"));          }             // Attempt to reuse session id if one was submitted in a cookie          // Do not reuse the session id if it is from a URL, to prevent possible          // phishing attacks          if (connector.getEmptySessionPath()                   && isRequestedSessionIdFromCookie()) {              session = manager.createSession(getRequestedSessionId());          } else {              session = manager.createSession(null);          }             // Creating a new session cookie based on that session          if ((session != null) && (getContext() != null)                 && getContext().getCookies()) {              Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,                                         session.getIdInternal());              configureSessionCookie(cookie);              response.addCookieInternal(cookie, context.getUseHttpOnly());          }             if (session != null) {              session.access();              return (session);          } else {              return (null);          }         }  

   

  通过以上代码,container或者返回一个已存在的session,或者新建一个session,或者返回Null(特殊情况).

      而session的创建是由manager完成的。Manager接口的实现根据具体情况有很多种,比如:

      StandardManager:默认的单机环境tomcat session manager.创建standardsession.

      DeltaSessionManager:适用于集群环境。创建DeltaSession

      ....

     不同的Manager管理不同具体类型的session,从而使得tomcat能够很好的支持集群环境下面的session复制,持久化 等等。比如DeltaSession创建session的时候,会向集群中的其他节点群播一个信息。

   

 

2. session的管理。

    大家都知道tomcat的session需要不断使超时的session失效,那么这个共是怎么实现的呢?

    在StandardContext启动的时候哦,会同时启动一个thread. 这个线程是一个daemon线程,

   会定时调用ManagerBase的一下方法:

  

Java代码  收藏代码 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++) {               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 );          }  

 

  很显然,session的有效性管理也通过session具体实现的。比如DeltaSession:

  

Java代码  收藏代码 public void expire(boolean notify, boolean notifyCluster) {          if (expiring)              return;          String expiredId = getIdInternal();             if(expiredId != null && manager != null &&             manager instanceof DeltaManager) {              DeltaManager dmanager = (DeltaManager)manager;              CatalinaCluster cluster = dmanager.getCluster();              ClusterMessage msg = dmanager.requestCompleted(expiredId, true);              if (msg != null) {                  if(dmanager.doDomainReplication()) {                      cluster.sendClusterDomain(msg);                  } else {                      cluster.send(msg);                  }              }          }             super.expire(notify);             if (notifyCluster) {              if (log.isDebugEnabled())                  log.debug(sm.getString("deltaSession.notifying",                                         ((ClusterManager)manager).getName(),                                          new Boolean(isPrimarySession()),                                          expiredId));              if ( manager instanceof DeltaManager ) {                  ( (DeltaManager) manager).sessionExpired(expiredId);              }          }      }  

 

    可以看出,当session失效的时候,manager会广播这个消息。