tomcat的session实现原理

服务器端实现原理

Session在服务器端具体是怎么实现的呢?我们使用session的时候一般都是这么使用的:

request.getSession()或者request.getSession(true)。

这个时候,服务器就检查是不是已经存在对应的Session对象,见HttpRequestBase类doGetSession(boolean create)方法:

if ((session != null) && !session.isValid()) session = null;
        if (session != null) return (session.getSession());

        // 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) {
                return (session.getSession());
            }
        }

 

requestSessionId从哪里来呢?这个肯定是通过Session实现机制的cookie或URL重写来设置的。见HttpProcessor类中的parseHeaders(SocketInputStream input):

for (int i = 0; i < cookies.length; i++) {
            if (cookies[i].getName().equals(Globals.SESSION_COOKIE_NAME)) {
                // Override anything requested in the URL
                if (!request.isRequestedSessionIdFromCookie()) {
                    // Accept only the first session id cookie
                    request.setRequestedSessionId(cookies[i].getValue());
                    request.setRequestedSessionCookie(true);
                    request.setRequestedSessionURL(false);

                }
            }
        }


或者HttpOrocessor类中的parseRequest(SocketInputStream input, OutputStream output)

// Parse any requested session ID out of the request URI
        int semicolon = uri.indexOf(match); // match 是";jsessionid="字符串
        if (semicolon >= 0) {
            String rest = uri.substring(semicolon + match.length());
            int semicolon2 = rest.indexOf(';');
            if (semicolon2 >= 0) {
                request.setRequestedSessionId(rest.substring(0, semicolon2));
                rest = rest.substring(semicolon2);
            } else {
                request.setRequestedSessionId(rest);
                rest = "";
            }
            request.setRequestedSessionURL(true);
            uri = uri.substring(0, semicolon) + rest;
            if (debug >= 1)
                log(" Requested URL session id is " + ((HttpServletRequest) request.getRequest()).getRequestedSessionId());
        } else {
            request.setRequestedSessionId(null);
            request.setRequestedSessionURL(false);
        }


里面的manager.findSession(requestSessionId)用于查找此会话ID对应的session对象。Tomcat实现
是通过一个HashMap实现,见ManagerBase.java的findSession(String id):

 if (id == null) return (null);
 synchronized (sessions) {
       Session session = (Session) sessions.get(id);
       return (session);
 }

 
Session本身也是实现为一个HashMap,因为Session设计为存放key-value键值对,Tomcat里面Session实现类是StandardSession,里面一个attributes属性:

 /**
 * The collection of user data attributes associated with this Session.
 */
 private HashMap attributes = new HashMap();

 
所 有会话信息的存取都是通过这个属性来实现的。Session会话信息不会一直在服务器端保存,超过一定的时间期限就会被删除,这个时间期限可以在 web.xml中进行设置,不设置的话会有一个默认值,在tomcat目录下conf/web.xml中找到 <session-config> 元素,Tomcat的默认值是60分钟。那么服务器端是怎么判断会话过期的呢?原理服务器会启动一个线程,一 直查询所有的Session对象,检查不活动的时间是否超过设定值,如果超过就将其删除。见StandardManager类,它实现了Runnable 接口,里面的run方法如下:

 /**
       * The background thread that checks for session timeouts and shutdown.
       */
       public void run() {
       
       // Loop until the termination semaphore is set
           while (!threadDone) {
               threadSleep();
               proces***pires();
           }
       
       }
       
       /**
       * Invalidate all sessions that have expired.
       */
       private void proces***pires() {
       
           long timeNow = System.currentTimeMillis();
           Session sessions[] = findSessions();
           
           for (int i = 0; i < sessions.length; i++) {
               StandardSession session = (StandardSession) sessions[i];
               if (!session.isValid())
               continue;
               int maxInactiveInterval = session.getMaxInactiveInterval();
               if (maxInactiveInterval < 0)
               continue;
               int timeIdle = // Truncate, do not round up
               (int) ((timeNow - session.getLastUsedTime()) / 1000L);
               if (timeIdle >= maxInactiveInterval) {
                   try {
                       expiredSessions++;
                       session.expire();
                   } catch (Throwable t) {
                       log(sm.getString("standardManager.expireException"), t);
                   }
               }
           }
       
       }

 
Session 信息在create,expire等事情的时候都会触发相应的Listener事件,从而可以对session信息进行监控,这些Listener只需要 继承HttpSessionListener,并配置在web.xml文件中。如下是一个监控在线会话数的Listerner:

public class MySessionListener implements HttpSessionListener {

    public void sessionCreated(HttpSessionEvent event) {

        HttpSession session = event.getSession();

        ServletContext application = session.getServletContext();

        // 在application范围由一个HashSet集保存所有的session

        HashSet sessions = (HashSet) application.getAttribute("sessions");

        if (sessions == null) {

            sessions = new HashSet();

            application.setAttribute("sessions", sessions);

        }

        // 新创建的session均添加到HashSet集中

        sessions.add(session);

        // 可以在别处从application范围中取出sessions集合

        // 然后使用sessions.size()获取当前活动的session数,即为“在线人数”

    }

    public void sessionDestroyed(HttpSessionEvent event) {

        HttpSession session = event.getSession();

        ServletContext application = session.getServletContext();

        HashSet sessions = (HashSet) application.getAttribute("sessions");

        // 销毁的session均从HashSet集中移除

        sessions.remove(session);

    }

}

猜你喜欢

转载自zql3315.iteye.com/blog/2308514