Tomcat源码---Session的分析一

一,前面看了大致的tomcat的请求/响应,接下来的文章对tomcat里面的一些模块进行详细分析,从中学习其思想...

Session的功能(Session 对象可以存储特定用户会话所需的信息):

  1,session是一个以bean的形式存在的,存储在内存中,特定用户可对其进行crud操作.

  2,session是有生命周期的.

  3,sesson是通过特定用户访问系统时,返回给一个jsessionid来进行识别用户的

----------------------------------------------------------------------------------------------------------------------

1,当客户端发送请求时,如果在头部文件中有传送jsessionid(生成jsessionid是在下面一部分讲),则执行以下步骤

执行到请求时会执行这个方法CoyoteAdapter#service...方法下的这步时if (postParseRequest(req, request, res, response)) 

对sessionid进行了操作,如下:

/**
     * Parse additional request parameters.
     */
    protected boolean postParseRequest(org.apache.coyote.Request req, 
                                       Request request,
    		                       org.apache.coyote.Response res, 
                                       Response response)
            throws Exception {
        //代码略
    
        // Parse session Id(解析;url=;jsessionid=)
        parseSessionId(req, request);

       //代码略       

        // Parse session Id(解决cookie中所带的jsessionid=)
        parseSessionCookiesId(req, request);

        return true;
    }

   1,parseSessiond(req,request)

    /**

* Parse session id in URL.
     */
    protected void parseSessionId(org.apache.coyote.Request req, Request request) {

        ByteChunk uriBC = req.requestURI().getByteChunk();
        //分析url上是否带有jsessionid
        int semicolon = uriBC.indexOf(match, 0, match.length(), 0);

        if (semicolon > 0) {
     
            // Parse session ID, and extract it from the decoded request URI
            int start = uriBC.getStart();
            int end = uriBC.getEnd();

            int sessionIdStart = semicolon + match.length();
            int semicolon2 = uriBC.indexOf(';', sessionIdStart);
            if (semicolon2 >= 0) {
                request.setRequestedSessionId
                    (new String(uriBC.getBuffer(), start + sessionIdStart, 
                            semicolon2 - sessionIdStart));
                // Extract session ID from request URI
                byte[] buf = uriBC.getBuffer();
                for (int i = 0; i < end - start - semicolon2; i++) {
                    buf[start + semicolon + i] 
                        = buf[start + i + semicolon2];
                }
                uriBC.setBytes(buf, start, end - start - semicolon2 + semicolon);
            } else {
                request.setRequestedSessionId
                    (new String(uriBC.getBuffer(), start + sessionIdStart, 
                            (end - start) - sessionIdStart));
                uriBC.setEnd(start + semicolon);
            }
            //如果有jsessionid,设置到request中
            request.setRequestedSessionURL(true);

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

    }

  2,parseSessionCookiesId(req, request);

/**
     * Parse session id in cookie
     */
    protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {

        // If session tracking via cookies has been disabled for the current
        // context, don't go looking for a session ID in a cookie as a cookie
        // from a parent context with a session ID may be present which would
        // overwrite the valid session ID encoded in the URL
        Context context = (Context) request.getMappingData().context;
        if (context != null && !context.getCookies())
            return;

        // Parse session id from cookies
        //获取cookies
        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)) {
                // Override anything requested in the URL
                if (!request.isRequestedSessionIdFromCookie()) {
                    // Accept only the first session id cookie
                    convertMB(scookie.getValue());
                    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()) {
                        // Replace the session id until one is valid
                        convertMB(scookie.getValue());
                        //在进行查找jsessionid如果有的话,设置进request
                        request.setRequestedSessionId
                            (scookie.getValue().toString());
                    }
                }
            }
        }

    }

由以上可知如果客户端有访问过系统的话,客户端的jsessionid将被设置进request中

  ----------------------------------------------------------------------------------------

在StandardManager是用来管理Session类的,首先是StandardManager的实例化是在

StandardContext#start

// Acquire clustered manager
                Manager contextManager = null;
                if (manager == null) {
                    if ( (getCluster() != null) && distributable) {
                        try {
                            contextManager = getCluster().createManager(getName());
                        } catch (Exception ex) {
                            log.error("standardContext.clusterFail", ex);
                            ok = false;
                        }
                    } else {
                        contextManager = new StandardManager();
                    }
                }

-------------------------------------------------------------------------------

 现在查看的是Session的生成过程..request.getSession()

/**
     * Return the session associated with this Request, creating one
     * if necessary.
     */
    public HttpSession getSession() {
        Session session = doGetSession(true);
        if (session != null) {
            return session.getSession();
        } else {
            return null;
        }
    }

 doGetSession(true)

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
        //StandardContext#start时进行的初始化
        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) { 
               //更新时间,使其生命周期重设为30(默认)
                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
        //判断session cookie的存储路径
        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);
            //在响应时,返回session cookie,这样在客户端就接收到一个jsessionid
            response.addCookieInternal(cookie, context.getUseHttpOnly());
        }

        if (session != null) {
            //如上
            session.access();
            return (session);
        } else {
            return (null);
        }

    }
 

 由上面的代码可以看出,sesion是先查找再创建,,,

ManagerBase#findSession

public Session findSession(String id) throws IOException {

        if (id == null)
            return (null);
      //sessions(ConcurrentHashMap<String, Session>)进行读取
        return (Session) sessions.get(id);

    }

 #createSession

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);
        if (sessionId == null) {
          //产生一个jsessionid
            sessionId = generateSessionId();
        // FIXME WHy we need no duplication check?
        /*         
             synchronized (sessions) {
                while (sessions.get(sessionId) != null) { // Guarantee
                    // uniqueness
                    duplicates++;
                    //产生一个jsessionid
                    sessionId = generateSessionId();
                }
            }
        */
            
            // FIXME: Code to be used in case route replacement is needed
            /*
        } else {
            String jvmRoute = getJvmRoute();
            if (getJvmRoute() != null) {
                String requestJvmRoute = null;
                int index = sessionId.indexOf(".");
                if (index > 0) {
                    requestJvmRoute = sessionId
                            .substring(index + 1, sessionId.length());
                }
                if (requestJvmRoute != null && !requestJvmRoute.equals(jvmRoute)) {
                    sessionId = sessionId.substring(0, index) + "." + jvmRoute;
                }
            }
            */
        }
        session.setId(sessionId);
        sessionCounter++;

        return (session);

 故一整个流程是:

 当客户端有请求session时request.getSession(),并生成一个jsessionid cookie返回给客户端,以后每次访问都得带着这个jsessionid过来,进行对ConcurrentHashMap<String, Session>进行识别.

------------------------------------------------------------------------------------------------------------------

还有一个问题就是当服务器突然中断掉时,重新启动时,未过期的session是可以重新加载的...

StandardManager#start()方法。最后调用了#load()方法.

public void load() throws ClassNotFoundException, IOException {
        if (SecurityUtil.isPackageProtectionEnabled()){
            try{
                AccessController.doPrivileged( new PrivilegedDoLoad() );
            } catch (PrivilegedActionException ex){
                Exception exception = ex.getException();
                if (exception instanceof ClassNotFoundException){
                    throw (ClassNotFoundException)exception;
                } else if (exception instanceof IOException){
                    throw (IOException)exception;
                }
                if (log.isDebugEnabled())
                    log.debug("Unreported exception in load() "
                        + exception);
            }
        } else {
            doLoad();
        }
    }

# doLoad()

protected void doLoad() throws ClassNotFoundException, IOException {
        if (log.isDebugEnabled())
            log.debug("Start: Loading persisted sessions");

        // Initialize our internal data structures
        sessions.clear();

        // Open an input stream to the specified pathname, if any
       // %CATALINA_HOME%/localhost/%app_name% /SESSIONS.ser文件,每一个应用下都有这个sessions.ser
       //可以从这里还原未过期的session
        File file = file();
        if (file == null)
            return;
        if (log.isDebugEnabled())
            log.debug(sm.getString("standardManager.loading", pathname));
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        Loader loader = null;
        ClassLoader classLoader = null;
        try {
            fis = new FileInputStream(file.getAbsolutePath());
            BufferedInputStream bis = new BufferedInputStream(fis);
            if (container != null)
                loader = container.getLoader();
            if (loader != null)
                classLoader = loader.getClassLoader();
            if (classLoader != null) {
                if (log.isDebugEnabled())
                    log.debug("Creating custom object input stream for class loader ");
                ois = new CustomObjectInputStream(bis, classLoader);
            } else {
                if (log.isDebugEnabled())
                    log.debug("Creating standard object input stream");
                ois = new ObjectInputStream(bis);
            }
        } catch (FileNotFoundException e) {
            if (log.isDebugEnabled())
                log.debug("No persisted data file found");
            return;
        } catch (IOException e) {
            log.error(sm.getString("standardManager.loading.ioe", e), e);
            if (ois != null) {
                try {
                    ois.close();
                } catch (IOException f) {
                    ;
                }
                ois = null;
            }
            throw e;
        }

        // Load the previously unloaded active sessions
        synchronized (sessions) {
            try {
                Integer count = (Integer) ois.readObject();
                int n = count.intValue();
                if (log.isDebugEnabled())
                    log.debug("Loading " + n + " persisted sessions");
                for (int i = 0; i < n; i++) {
                    StandardSession session = getNewSession();
                    session.readObjectData(ois);
                    session.setManager(this);
                    sessions.put(session.getIdInternal(), session);
                    session.activate();
                    sessionCounter++;
                }
            } catch (ClassNotFoundException e) {
                log.error(sm.getString("standardManager.loading.cnfe", e), e);
                if (ois != null) {
                    try {
                        ois.close();
                    } catch (IOException f) {
                        ;
                    }
                    ois = null;
                }
                throw e;
            } catch (IOException e) {
                log.error(sm.getString("standardManager.loading.ioe", e), e);
                if (ois != null) {
                    try {
                        ois.close();
                    } catch (IOException f) {
                        ;
                    }
                    ois = null;
                }
                throw e;
            } finally {
                // Close the input stream
                try {
                    if (ois != null)
                        ois.close();
                } catch (IOException f) {
                    // ignored
                }

                // Delete the persistent storage file
                if (file != null && file.exists() )
                    file.delete();
            }
        }

        if (log.isDebugEnabled())
            log.debug("Finish: Loading persisted sessions");
    }
 

相关推荐