Tomcat源码系列5--Tomcat的Session生成和管理1

本次主要谈一下Tomcat中Session的生成和管理情况。

一.       关于Sessincookie

session被用于表示一个持续的连接状态,在网站访问中一般指代客户端浏览器的进程从开启到结束的过程。

session的常见实现形式有两种:

1.     会话cookie:即未设置过期时间的cookie,这个cookie的默认生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。

2.     持久cookie:是指存放于客户端硬盘中的 cookie信息(设置了一定的有效期限),它不会随着浏览器的关闭而消失。

实现机制是当用户发起一个请求的时候,服务器会检查该请求中是否包含sessionid,如果未包含,则系统会创造一个名为JSESSIONID的输出cookie返回给浏览器,并将其以HashTable的形式写到服务器的内存里面; 当已经包含sessionid是,服务端会检查找到与该session相匹配的信息,如果存在则直接使用该sessionid,若不存在则重新生成新的session。

二. Session的生成

1.当tomcat处理请求时会调用org.apache.catalina.connector.CoyoteAdapter.service()方法。

(org.apache.catalina.connector.CoyoteAdapter.service) 
public void service(org.apache.coyote.Request req, 
                    org.apache.coyote.Response res) 
        throws Exception { 
        ……//此处省略一部分代码 
        try { 

           
            if ( postParseRequest(req, request, res, response) ) { 
  
                connector.getContainer().getPipeline().getFirst().invoke(request, response); 
            } 

            response.finishResponse(); 
            req.action( ActionCode.ACTION_POST_REQUEST , null); 

        } catch (IOException e) { 
            ; 
        } catch (Throwable t) { 
            log.error(sm.getString("coyoteAdapter.service"), t); 
        } finally { 

            request.recycle(); 
            response.recycle(); 
        } 
    }

 

此时CoyoteAdapter.service()方法会调用postParseRequest()方法。

2.postParseRequest()方法会分别调用parseSessionId(),parseSessionCookiesId()两个方法对JSessionid进行解析。

org.apache.catalina.connector.CoyoteAdapter.postParseRequest() 
protected boolean postParseRequest(org.apache.coyote.Request req, 
                               Request request, 
                               org.apache.coyote.Response res, 
   Response response) 
        throws Exception { 

        //此处省略部分代码 

        // 解析session Id 
        parseSessionId(req, request);§2.1 

//此处省略部分代码 

        // 解析session Id 
        parseSessionCookiesId(req, request); §2.2 

        return true; 
    }

 

§2.1

从URL中读取sessionID。

(org.apache.catalina.connector.CoyoteAdapter.parseSessionId) 
protected void parseSessionId(org.apache.coyote.Request req, Request request) { 

        CharChunk uriCC = req.decodedURI().getCharChunk(); 
         // 判断URL中是否有“;jsessionid=”这个字符串 
        int semicolon = uriCC.indexOf(match, 0, match.length(), 0); 

        if (semicolon > 0) { 

            // 解析 session ID,并从URL中提取它 
            int start = uriCC.getStart(); 
            int end = uriCC.getEnd(); 

            int sessionIdStart = start + semicolon + match.length(); 
            int semicolon2 = uriCC.indexOf(';', sessionIdStart); 
            if (semicolon2 >= 0) { 
                request.setRequestedSessionId 
                    (new String(uriCC.getBuffer(), sessionIdStart, 
                                semicolon2 - semicolon - match.length())); 
            } else { 
                request.setRequestedSessionId 
                    (new String(uriCC.getBuffer(), sessionIdStart, 
                                end - sessionIdStart)); 
            } 
            request.setRequestedSessionURL(true); 

            //从请求的URL中提取session ID 
            ByteChunk uriBC = req.requestURI().getByteChunk(); 
            start = uriBC.getStart(); 
            end = uriBC.getEnd(); 
            semicolon = uriBC.indexOf(match, 0, match.length(), 0); 

            if (semicolon > 0) { 
                sessionIdStart = start + semicolon; 
                semicolon2 = uriCC.indexOf 
                    (';', start + semicolon + match.length()); 
                uriBC.setEnd(start + semicolon); 
                byte[] buf = uriBC.getBuffer(); 
                if (semicolon2 >= 0) { 
                    for (int i = 0; i < end - start - semicolon2; i++) { 
                        buf[start + semicolon + i] 
                            = buf[start + i + semicolon2]; 
                    } 
                    uriBC.setBytes(buf, start, semicolon 
                                   + (end - start - semicolon2)); 
                } 
            } 

        } else { 
            request.setRequestedSessionId(null); 
            request.setRequestedSessionURL(false); 
        } 

    }

 

§2.2

从Cookie中读取sessionID。

(org.apache.catalina.connector.CoyoteAdapter.parseSessionCookiesId)
protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) { 

        // 从cookie中解析session ID 
        Cookies serverCookies = req.getCookies(); 
        int count = serverCookies.getCookieCount(); 
        if (count <= 0) 
            return; 
        for (int i = 0; i < count; i++) { 
            ServerCookie scookie = serverCookies.getCookie(i); 
            if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) { 
                     //重写URL中的任何请求 
                if (!request.isRequestedSessionIdFromCookie()) { 
                    // 只接受第一个session ID的Cookie 
                    request.setRequestedSessionId 
                        (scookie.getValue().toString()); 
                    request.setRequestedSessionCookie(true); 
                    request.setRequestedSessionURL(false); 
                    if (log.isDebugEnabled()) 
                        log.debug(" Requested cookie session id is " + 
                            request.getRequestedSessionId()); 
                } else { 
                    if (!request.isRequestedSessionIdValid()) { 
                        //替换,直到一个会话ID是有效的 
                        request.setRequestedSessionId 
                            (scookie.getValue().toString()); 
                    }}}}}

当parseSessionId()、parseSessionCookiesId()两个方法调用结束后,postParseRequest方法也调用结束,此时session ID的获取过程结束,下一步程序会根据session ID来得到session。

3.当要生成一个session对象时,getSession(booleancreate)方法将提高调用。

(org.apache.catalina.connector.Request.getSession(boolean create)) 
public HttpSession getSession(boolean create) { 
        Session session = doGetSession(create); 
        if (session != null) { 
            return session.getSession(); 
        } else { 
            return null; 
        } 
    }

 

4.Request#doGetSession被调用,方法就sessionid进行查找,查找成功则返回session对象,查找不成功就创建一个新的session对象,并在响应客户请求时创建sessionCookie发给至客户端。

(org.apache.catalina.connector.Request.doGetSession(boolean create)) 
protected Session doGetSession(boolean create) { 
        //context不存在就直接返回null 
        if (context == null) 
            return (null); 
        //判断session是否有效 
        if ((session != null) && !session.isValid()) 
            session = null; 
        if (session != null) 
            return (session); 

        //如果存在和有效返回请求的session 
        Manager manager = null; 
        if (context != null) 
            manager = context.getManager(); 
        if (manager == null) 
            return (null);      // Sessions 不被支持 

         // 判断是否有SessionID 
        if (requestedSessionId != null) { 
            try { 
// 在manager中根据sessionid查找session 
                session = manager.findSession(requestedSessionId);※1 
            } catch (IOException e) { 
                session = null; 
            } 
            if ((session != null) && !session.isValid()) 
                session = null; 
            if (session != null) { 
                 //更新访问时间 
                session.access(); 
                return (session); 
            } 
        } 

        // 如果请求和响应没有被提交就创建一个新的session 
        if (!create) 
            return (null); 
        if ((context != null) && (response != null) && 
            context.getCookies() && 
            response.getResponse().isCommitted()) { 
            throw new IllegalStateException 
              (sm.getString("coyoteRequest.sessionCreateCommitted")); 
        } 

        session = manager.createSession();※2 

        //根据session创建一个新的session Cookie 
        if ((session != null) && (getContext() != null) 
               && getContext().getCookies()) { 
            Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME, 
                                       session.getId()); 
         // 配置Session Cookie 
            configureSessionCookie(cookie); 
         // 在响应中加入Session Cookie 
            response.addCookie(cookie); 
        } 

        if (session != null) { 
           //更新访问时间 
            session.access(); 
            return (session); 
        } else { 
            return (null); 
        } 

    }

 

※1

根据传过来的id参数对session进行查找,查找即返回session对象,若没查找到就创建一个新的session对象。

(org.apache.catalina.session.ManagerBase.findSession(String id)) 
public Session findSession(String id) throws IOException { 

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

    }

 

※2

ManagerBase#createSession()被调用,创建一个新的session。

(org.apache.catalina.session.ManagerBase.createSession()) 
public Session createSession() { 

        //回收或创建一个session实例 
        Session session = createEmptySession(); 

        //初始化新session的属性并返回 

        session.setNew(true); 
        session.setValid(true); 
        session.setCreationTime(System.currentTimeMillis()); 
        session.setMaxInactiveInterval(this.maxInactiveInterval); 
        String sessionId = generateSessionId(); 
        session.setId(sessionId); 
        sessionCounter++; 

        return (session); 

    }

未完待续,下面谈一谈Session管理方面的内容。 

相关推荐