`
haitaoandroid
  • 浏览: 27729 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Tomcat源码分析(九)--Session管理

 
阅读更多
在明白Tomcat的Session机制之前,先要了解Session,Cookie,JSESSIONID这几个概念。JSESSIONID是一个唯一标识号,用来标识服务器端的Session,也用来标识客户端的Cookie,客户端和服务器端通过这个JSESSIONID来一一对应。这里需要说明的是Cookie已经包含JSESSIONID了,可以理解为JSESSIONID是Cookie里的一个属性。让我假设一次客户端连接来说明我对个这三个概念的理解:
Http连接本身是无状态的,即前一次发起的连接跟后一次没有任何关系,是属于两次独立的连接请求,但是互联网访问基本上都是需要有状态的,即服务器需要知道两次连接请求是不是同一个人访问的。如你在浏览淘宝的时候,把一个东西加入购物车,再点开另一个商品页面的时候希望在这个页面里面的购物车还有上次添加进购物车的商品。也就是说淘宝服务器会知道这两次访问是同一个客户端访问的。
客户端第一次请求到服务器连接,这个连接是没有附带任何东西的,没有Cookie,没有JSESSIONID。服务器端接收到请求后,会检查这次请求有没有传过来JSESSIONID或者Cookie,如果没有JSESSIONID和Cookie,则服务器端会创建一个Session,并生成一个与该Session相关联的JSESSIONID返回给客户端,客户端会保存这个JSESSIONID,并生成一个与该JSESSIONID关联的Cookie,第二次请求的时候,会把该Cookie(包含JSESSIONID)一起发送给服务器端,这次服务器发现这个请求有了Cookie,便从中取出JSESSIONID,然后根据这个JSESSIONID找到对应的Session,这样便把Http的无状态连接变成了有状态的连接。但是有时候浏览器(即客户端)会禁用Cookie,我们知道Cookie是通过Http的请求头部的一个cookie字段传过去的,如果禁用,那么便得不到这个值,JSESSIONID便不能通过Cookie传入服务器端,当然我们还有其他的解决办法,url重写和隐藏表单,url重写就是把JSESSIONID附带在url后面传过去。隐藏表单是在表单提交的时候传入一个隐藏字段JSESSIONID。这两种方式都能把JSESSIONID传过去。
下面来看Tomcat是怎么实现以上流程的。从Tomcat源码分析(二)--连接处理可知,连接请求会交给HttpProcessor的process方法处理,在此方法有这么几句:
                    parseConnection(socket);
                    parseRequest(input, output);//解析请求行,如果有jessionid,会在方法里面解析jessionid
                    if (!request.getRequest().getProtocol()
                        .startsWith("HTTP/0"))
                        parseHeaders(input);//解析请求头部,如果有cookie字段,在方法里面会解析cookie,
下面看parseRequest方法里面是怎么解析jessionid的,这种解析方式是针对url重写的:

parseRequest方法:
int semicolon = uri.indexOf(match);//match是“;JSESSIONID=”,即在请求行查找字段JSESSIONID
        if (semicolon >= 0) {                                   //如果有JSESSIONID字段,表示不是第一次访问
            String rest = uri.substring(semicolon + match.length());
            int semicolon2 = rest.indexOf(';');
            if (semicolon2 >= 0) {
                request.setRequestedSessionId(rest.substring(0, semicolon2));//设置sessionid
                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 {                               //如果请求行没有JSESSIONID字段,表示是第一次访问。
            request.setRequestedSessionId(null);
            request.setRequestedSessionURL(false);
        }

代码没什么说的,看url有没有JSESSIONID,有就设置request的sessionid,没有就设置为null。有再看parseHeaders方法:
parseHeaders方法:
.....
....else if (header.equals(DefaultHeaders.COOKIE_NAME)) { //COOKIE_NAME的值是cookie
                Cookie cookies[] = RequestUtil.parseCookieHeader(value);
                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());//设置sessionid
                            request.setRequestedSessionCookie(true);
                            request.setRequestedSessionURL(false);
                            if (debug >= 1)
                                log(" Requested cookie session id is " +
                                    ((HttpServletRequest) request.getRequest())
                                    .getRequestedSessionId());
                        }
                    }
                    if (debug >= 1)
                        log(" Adding cookie " + cookies[i].getName() + "=" +
                            cookies[i].getValue());
                    request.addCookie(cookies[i]);
                }
            } 
代码主要就是从http请求头部的字段cookie得到JSESSIONID并设置到reqeust的sessionid,没有就不设置。这样客户端的JSESSIONID(cookie)就传到tomcat,tomcat把JSESSIONID的值赋给request了。这个request在Tomcat的唯一性就标识了。
我们知道,Session只对应用有用,两个应用的Session一般不能共用,在Tomcat一个Context代表一个应用,所以一个应用应该有一套自己的Session,Tomcat使用Manager来管理各个应用的Session,Manager也是一个组件,跟Context是一一对应的关系,怎么关联的请参考Tomcat源码分析(一)--服务启动,方法类似。Manager的标准实现是StandardManager,由它统一管理Context的Session对象(标准实现是StandardSession),能够猜想,StandardManager一定能够创建Session对象和根据JSESSIONID从跟它关联的应用中查找Session对象。事实上StandardManager确实有这样的方法,但是StandardManager本身没有这两个方法,它的父类ManagerBase有这两个方法
ManagerBase类的findSession和createSession()方法
public Session findSession(String id) throws IOException {
        if (id == null)
            return (null);
        synchronized (sessions) {
            Session session = (Session) sessions.get(id);//根据sessionid(即JSESSIONID)查找session对象。
            return (session);
        }
    }
public Session createSession() { //创建session对象
        // Recycle or create a Session instance
        Session session = null;
        synchronized (recycled) {
            int size = recycled.size();
            if (size > 0) {
                session = (Session) recycled.get(size - 1);
                recycled.remove(size - 1);
            }
        }
        if (session != null)
            session.setManager(this);
        else
            session = new StandardSession(this);

        // Initialize the properties of the new session and return it
        session.setNew(true);
        session.setValid(true);
        session.setCreationTime(System.currentTimeMillis());
        session.setMaxInactiveInterval(this.maxInactiveInterval);
        String sessionId = generateSessionId();//使用md5算法生成sessionId
        String jvmRoute = getJvmRoute();
        // @todo Move appending of jvmRoute generateSessionId()???
        if (jvmRoute != null) {
            sessionId += '.' + jvmRoute;
            session.setId(sessionId);
        }
        session.setId(sessionId);
        return (session);
    }
以上是StandardManager的管理Session的两个重要方法。这里有一个问题,Session是在什么时候生成的?仔细想想,我们编写servlet的时候,如果需要用到Session,会使用request.getSession(),这个方法最后会调用到HttpRequestBase的getSession()方法,所以这里有个重要的点:Session并不是在客户端第一次访问就会在服务器端生成,而是在服务器端(一般是servlet里)使用request调用getSession方法才生成的。但是默认情况下,jsp页面会调用request.getSession(),即jsp页面的这个属性<%@ page session="true" %>默认是true的,编译成servlet后会调用request.getSession()。所以只要访问jsp页面,一般是会在服务器端创建session的。但是在servlet里就需要显示的调用getSession(),当然是在要用session的情况。下面看这个getSession()方法:
HttpRequestBase.getSession()
   调用---------------》
    HttpRequestBase.getSession(boolean create)
       调用 ----------------》
            HttpRequestBase.doGetSession(boolean create){
      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.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);//这里调用StandardManager的findSession方法查找是否存在Session对象
            } catch (IOException e) {
                session = null;
            }
            if ((session != null) && !session.isValid())
                session = null;
            if (session != null) {
                return (session.getSession());
            }
        }

        // 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("httpRequestBase.createCommitted"));
        }

        session = manager.createSession();//这里调用StandardManager的创建Session对象
        if (session != null)
            return (session.getSession());
        else
            return (null);
}     

至此,Tomcat的Session管理的大部分东西也写的差不多了,这里没有写StandardManager和StandardSession两个类以及他们的实现接口,还有继承关系等,是因为觉得这篇文章已经够长了,而且他们跟跟其他标准组件也差不多,无非是实现的具体功能不一样,比如StandardSession还有过期处理等,不过它们也跟其他组件有各种关系,比如StandardManager就跟Context容器是关联的。有机会再细细的说Session管理器其他的东西(持久化和分布式)。
分享到:
评论

相关推荐

    tomcat-redis-session-manager源码

    首先,我们来看`Tomcat-Redis-Session-Manager`的核心功能:它将Tomcat默认的内存会话管理替换为基于Redis的分布式会话管理。这主要涉及两个关键组件:`RedisSessionManager`和`RedisSessionHandlerWrapper`。 `...

    tomcat-redis-session-manager-master

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

    tomcat-redis-session-manager-master.zip

    本文将围绕“tomcat-redis-session-manager-master.zip”这个压缩包中的源码,深入探讨如何使用Redis实现Tomcat的session共享,并提供详尽的知识点解析。 首先,我们要理解Tomcat的session管理基础。在默认情况下,...

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

    《深入理解Tomcat-Redis-Session-Manager:与Tomcat7的整合及源码解析》 在现代Web应用中,由于高并发、分布式部署的需求,单一服务器的session管理已无法满足需求。这时,将session存储在外部存储系统,如Redis,...

    tomcat源码,servlet-api源码

    Tomcat源码分析有助于识别性能瓶颈,例如调整线程池大小、缓存策略、连接器设置等。此外,了解如何配置和使用NIO或APR(Apache Portable Runtime)连接器可以显著提升Tomcat的并发处理能力。 8. **故障排查** ...

    tomcat8源码

    Apache Tomcat 8.5.23 源码分析 Apache Tomcat 是一个开源的、免费的Web服务器和Servlet容器,它实现了Java Servlet和JavaServer Pages(JSP)规范,是开发和部署Java Web应用的重要平台。深入理解Tomcat的源码有助...

    tomcat6源码分析

    《Tomcat6源码分析——深入理解Web服务器的运行机制》 Tomcat6作为Apache软件基金会的Jakarta项目的一部分,是一款广泛使用的Java Servlet容器,它实现了Java Servlet和JavaServer Pages(JSP)规范,为开发和部署...

    tomcat架构的源码分析

    ### Tomcat架构的源码分析 #### 一、Tomcat的架构概述 Tomcat作为一款广泛使用的开源Java Servlet容器,其内部架构设计简洁而高效。本文档将对Tomcat的架构进行详细介绍,并从源码层面深入分析其核心组成部分。...

    tomcat-7.0.90-src-源码

    【标题】"Tomcat 7.0.90 源码分析" 【内容】Apache Tomcat 是一个开源的、高性能的Java Servlet容器,它实现了Java EE中的Web应用服务器规范,特别是Servlet和JSP规范。这里我们关注的是Tomcat 7.0.90的源码,这个...

    tomcat源码

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

    Tomcat源码分析

    【标题】"Tomcat源码分析" 在深入探讨Tomcat源码之前,首先需要了解Tomcat是什么。Tomcat是一款开源的、基于Java的Web应用服务器,由Apache软件基金会开发。它实现了Java Servlet和JavaServer Pages(JSP)规范,...

    memcached-session-manager 实现 tomcat session共享

    **memcached-session-manager** 是一个开源项目,它实现了基于 Memcached 的 Tomcat Session 管理器。Memcached 是一个高性能、分布式的内存对象缓存系统,常用于减轻数据库的负载。将 Session 存储在 Memcached 中...

    tomcat7源码

    源码分析是提升开发者对服务器内部运作机制理解的重要途径,尤其对于Tomcat这样的核心组件,源码的学习能够帮助我们更深入地理解Web应用的部署、运行以及性能优化。 首先,我们要了解Tomcat的架构。Tomcat7基于...

    Tomcat集群——使用MSM管理集群Session

    4. **源码分析**: MSM的实现主要位于`org.apache.catalina.ha.session.DeltaManager`类中。深入源码可以帮助理解其内部的同步机制,如`replicateSession()`方法处理Session的复制,`checkSessions()`方法进行定期...

    tomcat源码基于6.0

    二、Tomcat源码分析 1. **启动流程**:从`bin/catalina.sh`或`bin/catalina.bat`开始,通过`org.apache.catalina.startup.Bootstrap`启动Catalina,加载配置文件,初始化服务器,最后启动主循环监听端口。 2. **...

    how tomcat works和jetty-src和tomcat7-src

    例如,分析请求处理链路、研究部署描述符解析、查看容器启动过程等,都是深入源码的好切入点。同时,可以尝试修改源码并构建自己的版本,以验证理解和实践能力。 总的来说,对于Java Web开发者来说,掌握Tomcat和...

    tomcat8:tomcat8源码分析(tomcat-8.0.32版本)

    5. **学习设计模式**:Tomcat源码中运用了多种设计模式,如工厂模式、单例模式、观察者模式等,学习源码有助于提升设计能力。 综上所述,对Tomcat 8.0.32版本的源码分析是一项深入理解其工作原理、优化性能和进行...

    Tomcat原理与实战

    例如,可以使用负载均衡器分发请求,确保日志集中管理和分析,以及设置故障转移机制以保证高可用性。 **3. 新特性** Tomcat随着版本更新不断引入新特性,如性能优化、安全增强、内存泄漏检测、支持更高级的SSL/TLS...

    apache-tomcat-8.5.51-src.7z

    Apache Tomcat是一款开源的Java Servlet容器,用于部署和运行Java Web应用程序。它是基于Sun Microsystems的Java Servlet...如果你对Java Web服务有深入的兴趣,那么分析和学习Tomcat源码将是一次富有价值的学习经历。

Global site tag (gtag.js) - Google Analytics