session

Recently, I used session together in my work, but there was a pit when I did not do the consistency of session. As a result, the online environment could only run on a single node.

The difference between session and cookie:

The session is stored on the server, while the cookie is stored on the client. The session is more secure and has a larger capacity than the cookie. Of course, sessions consume server resources, and cookies are less stressful to store on the client and server. Session A session is valid. The cookie time can be determined by you

Tomcat’s session

The browser sends a request to the server with a session ID. Tomcat uses this session ID to determine whether the browser has a session stored on the server.

Tomcat implementation is javax.mail. Servle. HTTP class or interface with the Request. The class, ApplicationHttpRequest. Class… .

Session is obtained in a servlet, usually by calling the getSession method of Request. This method requires passing in a Boolean argument that implements whether to create a new session or not if the JsessionID is empty or if no session object is available from the session pool.

The core code logic of Request.class;

protected Session doGetSession(boolean create) {...// Get the manager object of the context
        Manager manager = null;  
        if(context ! =null)  
            manager = context.getManager();  
        if (manager == null)  
            return (null);      // Sessions are not supported  
          
        // The requestedSessionId is parsed from the Http Request
        if(requestedSessionId ! =null) {  
            try {  
                // Find the corresponding session object in the session pool managed by manager
                session = manager.findSession(requestedSessionId);  
            } catch (IOException e) {  
                session = null;  
            }  
            // Determine whether the session is empty and whether it has expired or timed out
            if((session ! =null) && !session.isValid())  
                session = null;  
            if(session ! =null) {  
                // The session object is valid and records the access time
                session.access();  
                return(session); }}// If the parameter is false, no new session object is created
        if(! create)return (null);  
        if((context ! =null) && (response ! =null) &&  
            context.getCookies() &&  
            response.getResponse().isCommitted()) {  
            throw new IllegalStateException  
              (sm.getString("coyoteRequest.sessionCreateCommitted"));  
        }  
  
        // Start creating a new session object
        if (connector.getEmptySessionPath()   
                && isRequestedSessionIdFromCookie()) {  
            session = manager.createSession(getRequestedSessionId());  
        } else {  
            session = manager.createSession(null);  
        }  
  
        // Write the new session jsessionID to the cookie and pass it to browser
        if((session ! =null) && (getContext() ! =null)  
               && getContext().getCookies()) {  
            Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,  
                                       session.getIdInternal());  
            configureSessionCookie(cookie);  
            response.addCookieInternal(cookie);  
        }  
        // Records the latest session access time
        if(session ! =null) {  
            session.access();  
            return (session);  
        } else {  
            return (null); }}Copy the code

Here is the source code for ApplicationHttpRequest’s getSession

 /**
     * Return the session associated with this Request, creating one
     * if necessary and requested.
     *
     * @param create Create a new session if one does not exist
     */
    @Override
    public HttpSession getSession(boolean create) {

        if (crossContext) {

            // 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 there is a session, return it directly
            if(session ! =null && session.isValid()) {
                return (session.getSession());
            }

            HttpSession other = super.getSession(false);
            if (create && (other == null)) {
                // First create a session in the first context: the problem is
                // that the top level request is the only one which can
                // create the cookie safely
                other = super.getSession(true);
            }
            if(other ! =null) {
                Session localSession = null;
                try {
                    localSession =
                        context.getManager().findSession(other.getId());
                    if(localSession ! =null && !localSession.isValid()) {
                        localSession = null; }}catch (IOException e) {
                    // Ignore
                }
                if (localSession == null && create) {
                    localSession =
                        context.getManager().createSession(other.getId());
                }
                if(localSession ! =null) {
                    // Update the access time
                    localSession.access();
                    session = localSession;
                    returnsession.getSession(); }}return null;

        } else {
            return super.getSession(create); }}Copy the code

StandardSession

You can trace it back to StandardSession from ApplicationHttpRequest, and here’s some of the important source code for StandardSession, which I’ve copied for you to understand, You can see that it encapsulates a StandardSessionFacade that provides methods for external use, and the attributes inside is a concurrentHashMap.

    
    /** * The collection of user data attributes associated with this Session. */
    protected ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<>();	


	/** * Set the session identifier for this session@param id The new session identifier
     */
    @Override
    public void setId(String id) {
        setId(id, true);
    }


	/** * Return the HttpSession for which this object * is the facade. */
    @Override
    public HttpSession getSession(a) {

        if (facade == null) {if (SecurityUtil.isPackageProtectionEnabled()){
                final StandardSession fsession = this;
                facade = AccessController.doPrivileged(
                        new PrivilegedAction<StandardSessionFacade>(){
                    @Override
                    public StandardSessionFacade run(a){
                        return newStandardSessionFacade(fsession); }}); }else {
                facade = new StandardSessionFacade(this); }}return (facade);

    }
Copy the code

sessionId

setId(String id)

The setId(String ID) method in StandardSession is used to create a unique session ID flag for the session maker, using the call chain, which can be traced back to

ManagerBase class, below is part of the source of ManagerBase

 @Override
    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(getContext().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);

    }
Copy the code

Check the generateSessionId (); Below the sessionId generated source, through sessionIdGenerator. GenerateSessionId (); Generate a sessionId. SessionIdGenerator is an interface

      /** * The set of currently active Sessions for this Manager, keyed by * session identifier. */
    protected Map<String, Session> sessions = new ConcurrentHashMap<>();
  
 	 /**
     * Generate and return a new session identifier.
     * @return a new session id
     */
    protected String generateSessionId(a) {

        String result = null;

        do {
            if(result ! =null) {
                // Not thread-safe but if one of multiple increments is lost
                // that is not a big deal since the fact that there was any
                // duplicate is a much bigger issue.
                duplicates++;
            }

            result = sessionIdGenerator.generateSessionId();

        } while (sessions.containsKey(result));

        return result;
    }
Copy the code

The session expires

We can see the Access () method in doGetSession in the core source code, which updates our session login time. In standardSession.class we can see the isValid() method, which is used to determine whether this session is expired

/** * Return the isValid flag for this session. */
    @Override
    public boolean isValid(a) {

        if (!this.isValid) {
            return false;
        }

        if (this.expiring) {
            return true;
        }

		AccessCount = accessCount = accessCount = accessCount = accessCount = accessCount
        if (ACTIVITY_CHECK && accessCount.get() > 0) {
            return true;
        }

        // This is the procedure to determine whether the last access has timed out
        if (maxInactiveInterval > 0) {
            // Determine the last access time
            int timeIdle = (int) (getIdleTimeInternal() / 1000L);
            if (timeIdle >= maxInactiveInterval) {
                expire(true); }}return this.isValid;
    }

    /**
     * Return the idle time from last client access time without invalidation check
     * @see #getIdleTime()
     */
    @Override
    public long getIdleTimeInternal(a) {
        long timeNow = System.currentTimeMillis();
        long timeIdle;
        if (LAST_ACCESS_AT_START) {
            timeIdle = timeNow - lastAccessedTime;
        } else {
            timeIdle = timeNow - thisAccessedTime;
        }
        return timeIdle;
    }
Copy the code

Expire method is invoked to paste important source code

public void expire(boolean notify) {   
  
        synchronized (this) {...// Set the flag bit
            setValid(false);  
  
            // Calculate statistics such as the average lifetime of all sessions under this manager
            long timeNow = System.currentTimeMillis();  
            int timeAlive = (int) ((timeNow - creationTime)/1000);  
            synchronized (manager) {  
                if (timeAlive > manager.getSessionMaxAliveTime()) {  
                    manager.setSessionMaxAliveTime(timeAlive);  
                }  
                int numExpired = manager.getExpiredSessions();  
                numExpired++;  
                manager.setExpiredSessions(numExpired);  
                int average = manager.getSessionAverageAliveTime();  
                average = ((average * (numExpired-1)) + timeAlive)/numExpired;  
                manager.setSessionAverageAliveTime(average);  
            }  
  
            // Remove this session from the session pool of the Manager object
            manager.remove(this); . }}Copy the code