`
wu_quanyin
  • 浏览: 208145 次
  • 性别: Icon_minigender_1
  • 来自: 福建省
社区版块
存档分类
最新评论

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");
    }
 

 

 

 

 

2
0
分享到:
评论

相关推荐

    tomcat-redis-session-manager源码

    《深入解析Tomcat-Redis-Session-Manager源码》 在现代Web应用中,服务器端会话管理是一个至关重要的部分,特别是在高并发、分布式环境中。Tomcat作为最流行的Java Servlet容器,提供了丰富的功能来支持这一需求。...

    Tomcat8亲测可用 tomcat-redis-session-manager的jar包

    标题中的“Tomcat8亲测可用 tomcat-redis-session-manager的jar包”指的是一个专为Tomcat8设计的,用于管理session的扩展组件。这个组件实现了将Tomcat应用服务器中的用户session数据存储到Redis分布式缓存系统中,...

    tomcat8.5.20-redis-session共享-JAR包大全

    这个包里含有commons-pool2-2.4.2、jedis-2.9.0、tomcat85-session-redis-1.0三个主要JAR包。apache-tomcat-8.5.20.tar.gz源码包和context.xml文件,这套配置是我自己亲测可用的。。另外我用的redis4这个版本。注意...

    tomcat-redis-session-manager支持tomcat7

    因tomcat7使用redis共享session,其他的包存在问题,自己编译后处理通过。 该包是在https://github.com/jcoleman/tomcat-redis-session-manager 将源码编译后的包。

    tomcat8-redis-session共享

    标题 "tomcat8-redis-session共享" 涉及到的是在Tomcat 8中使用Redis作为Session共享存储的解决方案。这是一个常见的需求,特别是在分布式系统中,为了保持用户会话的一致性,需要将Session数据在多台服务器之间共享...

    tomcat-redis-session-manager-master

    "tomcat-redis-session-manager-master" 是一个项目名称,表明这是一个与Tomcat(一个流行的Java应用服务器)和Redis(一个开源的内存数据结构存储系统)相关的session管理器的主分支。通常,这样的项目是用来实现将...

    tomcat-redis-session-manager-master.jar

    自己通过源码编译后的jar包,已实验可以正常使用

    tomcat8 tomcat-redis-session-manager

    tomcat8下 tomcat-redis-session-manager , github上有源码,其他版本都有打好的jar包,tomcat 8 下没有,下载源码生成了一个。

    tomcat-redis-session-manager-master.zip

    通过上述分析,我们可以看到,Tomcat Redis Session Manager提供了便捷的session共享机制,有效地解决了高并发场景下的session管理问题。通过对源码的学习和实践,开发者可以更好地理解和掌握这一技术,提升系统的可...

    tomcat-cluster-redis-session-manager:Tomcat集群Redis会话管理器Java客户端

    Tomcat集群Redis会话管理器 Redis会话管理器是可插入的。 它将会话存储到Redis中,以便在Tomcat服务器群集之间轻松分配HTTP请求。 在这里,会话被实现为非粘性的(意味着,每个请求都可以转到集群中的任何服务器,...

    tomcat redis session.rar

    【描述】:“Tomcat集群Nginx使用Redis保证Session同步”这一场景中,通常是因为在多台Tomcat服务器组成的集群中,每个服务器各自维护独立的Session,当用户在集群中的不同服务器之间切换时,可能会导致Session丢失...

    tomcat-redis-session-manager 支持 tomcat7 ,包含源码和jar

    通过分析源码,我们可以看到它如何将Tomcat的session操作转化为对Redis的操作。例如,session的创建、读取、更新和删除等操作都会映射到Redis的对应命令。同时,源码中还可能包含了错误处理和性能优化的策略,这些都...

    redis-tomcat7-session共享配置文档及依赖包

    在构建高性能、高可用性的Web应用系统中,session共享是一个重要的环节,特别是在使用负载均衡和应用集群时。本文将详细讲解如何通过Redis实现Tomcat7的session共享,并介绍相关配置和依赖包。首先,我们来看一下...

    tomcat-redis-session

    `tomcat-redis-session`是针对Apache Tomcat服务器的一个插件,它的主要作用是将用户的session信息从Tomcat的内存中移出,存储到高性能的键值存储系统Redis中。这种架构使得session数据可以在多台Tomcat服务器之间...

    omcat-redis-session-manager的jar包-包含Tomcat7和Tomcat8

    描述提到的是一个基于"tomcat-redis-session-manager"源码编译生成的jar包,这意味着开发者或系统管理员可以直接将这个jar包引入到他们的Tomcat环境中,无需自己从源代码构建。压缩包内包含了针对Tomcat7和Tomcat8两...

    分布式集群Session共享 简单多tomcat8+redis的session共享实现

    这个项目提供了一个兼容不同Tomcat版本的Session管理器,使得我们可以方便地在不同版本的Tomcat上实现Session共享。 源码可以帮助我们深入了解Session共享的具体实现,包括如何序列化和反序列化Session对象,如何...

    tomcat-7.0.90-src-源码

    深入Tomcat源码,我们可以学习到以下关键知识点: 1. **Web应用部署**:Tomcat如何解析`WEB-INF/web.xml`配置文件,加载Servlet和Filter,以及如何根据`META-INF/context.xml`设置上下文参数。 2. **生命周期管理*...

    tomcat源码

    Apache Tomcat源码分析 Apache Tomcat是一款广泛应用的开源Java Servlet容器,它是Java EE Web应用程序的标准实现。Tomcat源码的深入理解对于Java Web开发者来说是至关重要的,它可以帮助我们了解HTTP服务器的工作...

Global site tag (gtag.js) - Google Analytics