`
marb
  • 浏览: 422353 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

tomcat架构分析 (Session管理)

 
阅读更多

出处:http://gearever.iteye.com

Session管理是JavaEE容器比较重要的一部分,在app中也经常会用到。在开发app时,我们只是获取一个session,然后向 session中存取数据,然后再销毁session。那么如何产生session,以及session池如何维护及管理,这些并没有在app涉及到。这 些工作都是由容器来完成的。
Tomcat中主要由每个context容器内的一个Manager对象来管理session。对于这个manager对象的实现,可以根据tomcat提供的接口或基类来自己定制,同时,tomcat也提供了标准实现。
在tomcat架构分析(容器类)中已经介绍过,在每个context对象,即web app都具有一个独立的manager对象。通过server.xml可以配置定制化的manager,也可以不配置。不管怎样,在生成context对 象时,都会生成一个manager对象。缺省的是StandardManager类,其类路径为:

引用
org.apache.catalina.session.StandardManager


Session对象也可以定制化实现,其主要实现标准servlet的session接口:

引用
javax.servlet.http.HttpSession


Tomcat也提供了标准的session实现:

引用
org.apache.catalina.session.StandardSession


本文主要就是结合消息流程介绍这两个类的实现,及session机制。
Session方面牵涉的东西还是蛮多的,例如HA,session复制是其中重要部分等,不过本篇主要从功能方面介绍session管理,有时间再说说扩展。
Session管理主要涉及到这几个方面:

  • 创建session
  • 注销session
  • 持久化及启动加载session



创建session
在具体说明session的创建过程之前,先看一下BS访问模型吧,这样理解直观一点。

  1. browser发送Http request;
  2. tomcat内核Http11Processor会从HTTP request中解析出“jsessionid”(具体的解析过程为先从request的URL中解析,这是为了有的浏览器把cookie功能禁止后,将 URL重写考虑的,如果解析不出来,再从cookie中解析相应的jsessionid),解析完后封装成一个request对象(当然还有其他的 http header);
  3. servlet中获取session,其过程是根据刚才解析得到的jsessionid(如果有的话),从session池 (session maps)中获取相应的session对象;这个地方有个逻辑,就是如果jsessionid为空的话(或者没有其对应的session对象,或者有 session对象,但此对象已经过期超时),可以选择创建一个session,或者不创建;
  4. 如果创建新session,则将session放入session池中,同时将与其相对应的jsessionid写入cookie通过Http response header的方式发送给browser,然后重复第一步。


以上是session的获取及创建过程。在servlet中获取session,通常是调用request的getSession方法。这个方法 需要传入一个boolean参数,这个参数就是实现刚才说的,当jsessionid为空或从session池中获取不到相应的session对象时,选 择创建一个新的session还是不创建。
看一下核心代码逻辑;

Java代码  收藏代码
  1. protected Session doGetSession(boolean create) {  
  2.   
  3.         ……  
  4.         // 先获取所在context的manager对象  
  5.         Manager manager = null;  
  6.         if (context != null)  
  7.             manager = context.getManager();  
  8.         if (manager == null)  
  9.             return (null);      // Sessions are not supported  
  10.           
  11.         //这个requestedSessionId就是从Http request中解析出来的  
  12.         if (requestedSessionId != null) {  
  13.             try {  
  14.                 //manager管理的session池中找相应的session对象  
  15.                 session = manager.findSession(requestedSessionId);  
  16.             } catch (IOException e) {  
  17.                 session = null;  
  18.             }  
  19.             //判断session是否为空及是否过期超时  
  20.             if ((session != null) && !session.isValid())  
  21.                 session = null;  
  22.             if (session != null) {  
  23.                 //session对象有效,记录此次访问时间  
  24.                 session.access();  
  25.                 return (session);  
  26.             }  
  27.         }  
  28.   
  29.         // 如果参数是false,则不创建新session对象了,直接退出了  
  30.         if (!create)  
  31.             return (null);  
  32.         if ((context != null) && (response != null) &&  
  33.             context.getCookies() &&  
  34.             response.getResponse().isCommitted()) {  
  35.             throw new IllegalStateException  
  36.               (sm.getString("coyoteRequest.sessionCreateCommitted"));  
  37.         }  
  38.   
  39.         // 开始创建新session对象  
  40.         if (connector.getEmptySessionPath()   
  41.                 && isRequestedSessionIdFromCookie()) {  
  42.             session = manager.createSession(getRequestedSessionId());  
  43.         } else {  
  44.             session = manager.createSession(null);  
  45.         }  
  46.   
  47.         // 将新session的jsessionid写入cookie,传给browser  
  48.         if ((session != null) && (getContext() != null)  
  49.                && getContext().getCookies()) {  
  50.             Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,  
  51.                                        session.getIdInternal());  
  52.             configureSessionCookie(cookie);  
  53.             response.addCookieInternal(cookie);  
  54.         }  
  55.         //记录session最新访问时间  
  56.         if (session != null) {  
  57.             session.access();  
  58.             return (session);  
  59.         } else {  
  60.             return (null);  
  61.         }  
  62.     }  


尽管不能贴出所有代码,但是上述的核心逻辑还是很清晰的。从中也可以看出,我们经常在servlet中这两种调用方式的不同;
新创建session

引用
request.getSession(); 或者request.getSession(true);


不创建session

引用
request.getSession(false);


接下来,看一下StandardManager的createSession方法,了解一下session的创建过程;

Java代码  收藏代码
  1. public Session createSession(String sessionId) {  
  2. 是个session数量控制逻辑,超过上限则抛异常退出  
  3.     if ((maxActiveSessions >= 0) &&  
  4.         (sessions.size() >= maxActiveSessions)) {  
  5.         rejectedSessions++;  
  6.         throw new IllegalStateException  
  7.             (sm.getString("standardManager.createSession.ise"));  
  8.     }  
  9.     return (super.createSession(sessionId));  
  10. }  


这个最大支持session数量maxActiveSessions是可以配置的,先不管这个安全控制逻辑,看其主逻辑,即调用其基类的createSession方法;

Java代码  收藏代码
  1. public Session createSession(String sessionId) {  
  2.           
  3.         // 创建一个新的StandardSession对象  
  4.         Session session = createEmptySession();  
  5.   
  6.         // Initialize the properties of the new session and return it  
  7.         session.setNew(true);  
  8.         session.setValid(true);  
  9.         session.setCreationTime(System.currentTimeMillis());  
  10.         session.setMaxInactiveInterval(this.maxInactiveInterval);  
  11.         if (sessionId == null) {  
  12.             //设置jsessionid  
  13.             sessionId = generateSessionId();  
  14.         }  
  15.         session.setId(sessionId);  
  16.         sessionCounter++;  
  17.         return (session);  
  18.     }  


关键是jsessionid的产生过程,接着看generateSessionId方法;

Java代码  收藏代码
  1. protected synchronized String generateSessionId() {  
  2.   
  3.         byte random[] = new byte[16];  
  4.         String jvmRoute = getJvmRoute();  
  5.         String result = null;  
  6.   
  7.         // Render the result as a String of hexadecimal digits  
  8.         StringBuffer buffer = new StringBuffer();  
  9.         do {  
  10.             int resultLenBytes = 0;  
  11.             if (result != null) {  
  12.                 buffer = new StringBuffer();  
  13.                 duplicates++;  
  14.             }  
  15.   
  16.             while (resultLenBytes < this.sessionIdLength) {  
  17.                 getRandomBytes(random);  
  18.                 random = getDigest().digest(random);  
  19.                 for (int j = 0;  
  20.                 j < random.length && resultLenBytes < this.sessionIdLength;  
  21.                 j++) {  
  22.                     byte b1 = (byte) ((random[j] & 0xf0) >> 4);  
  23.                     byte b2 = (byte) (random[j] & 0x0f);  
  24.                     if (b1 < 10)  
  25.                         buffer.append((char) ('0' + b1));  
  26.                     else  
  27.                         buffer.append((char) ('A' + (b1 - 10)));  
  28.                     if (b2 < 10)  
  29.                         buffer.append((char) ('0' + b2));  
  30.                     else  
  31.                         buffer.append((char) ('A' + (b2 - 10)));  
  32.                     resultLenBytes++;  
  33.                 }  
  34.             }  
  35.             if (jvmRoute != null) {  
  36.                 buffer.append('.').append(jvmRoute);  
  37.             }  
  38.             result = buffer.toString();  
  39.         //注意这个do…while结构  
  40.         } while (sessions.containsKey(result));  
  41.         return (result);  
  42.     }  


这里主要说明的不是生成jsessionid的算法了,而是这个do…while结构。把这个逻辑抽象出来,可以看出;

如图所示,创建jsessionid的方式是由tomcat内置的加密算法算出一个随机的jsessionid,如果此jsessionid已经存在,则重新计算一个新的,直到确保现在计算的jsessionid唯一。
好了,至此一个session就这么创建了,像上面所说的,返回时是将jsessionid以HTTP response的header:“Set-cookie”发给客户端。
注销session

  • 主动注销
  • 超时注销


Session创建完之后,不会一直存在,或是主动注销,或是超时清除。即是出于安全考虑也是为了节省内存空间等。例如,常见场景:用户登出系统时,会主动触发注销操作。
主动注销
主动注销时,是调用标准的servlet接口:

引用
session.invalidate();


看一下tomcat提供的标准session实现(StandardSession)

Java代码  收藏代码
  1. public void invalidate() {  
  2.         if (!isValidInternal())  
  3.             throw new IllegalStateException  
  4.                 (sm.getString("standardSession.invalidate.ise"));  
  5.         // 明显的注销方法  
  6.         expire();  
  7.     }  


Expire方法的逻辑稍后再说,先看看超时注销,因为它们调用的是同一个expire方法。
超时注销
Tomcat定义了一个最大空闲超时时间,也就是说当session没有被操作超过这个最大空闲时间时间时,再次操作这个session,这个session就会触发expire。
这个方法封装在StandardSession中的isValid()方法内,这个方法在获取这个request请求对应的session对象时 调用,可以参看上面说的创建session环节。也就是说,获取session的逻辑是,先从manager控制的session池中获取对应 jsessionid的session对象,如果获取到,就再判断是否超时,如果超时,就expire这个session了。
看一下tomcat提供的标准session实现(StandardSession)

Java代码  收藏代码
  1. public boolean isValid() {  
  2.         ……  
  3.         //这就是判断距离上次访问是否超时的过程  
  4.         if (maxInactiveInterval >= 0) {   
  5.             long timeNow = System.currentTimeMillis();  
  6.             int timeIdle = (int) ((timeNow - thisAccessedTime) / 1000L);  
  7.             if (timeIdle >= maxInactiveInterval) {  
  8.                 expire(true);  
  9.             }  
  10.         }  
  11.         return (this.isValid);  
  12.     }  


Expire方法
是时候来看看expire方法了。

Java代码  收藏代码
  1. public void expire(boolean notify) {   
  2.   
  3.         synchronized (this) {  
  4.             ......  
  5.             //设立标志位  
  6.             setValid(false);  
  7.   
  8.             //计算一些统计值,例如此manager下所有session平均存活时间等  
  9.             long timeNow = System.currentTimeMillis();  
  10.             int timeAlive = (int) ((timeNow - creationTime)/1000);  
  11.             synchronized (manager) {  
  12.                 if (timeAlive > manager.getSessionMaxAliveTime()) {  
  13.                     manager.setSessionMaxAliveTime(timeAlive);  
  14.                 }  
  15.                 int numExpired = manager.getExpiredSessions();  
  16.                 numExpired++;  
  17.                 manager.setExpiredSessions(numExpired);  
  18.                 int average = manager.getSessionAverageAliveTime();  
  19.                 average = ((average * (numExpired-1)) + timeAlive)/numExpired;  
  20.                 manager.setSessionAverageAliveTime(average);  
  21.             }  
  22.   
  23.             // 将此session从manager对象的session池中删除  
  24.             manager.remove(this);  
  25.             ......  
  26.         }  
  27.     }  


不需要解释,已经很清晰了。
这个超时时间是可以配置的,缺省在tomcat的全局web.xml下配置,也可在各个app下的web.xml自行定义;

Xml代码  收藏代码
  1. <session-config>  
  2.     <session-timeout>30</session-timeout>  
  3. </session-config>  


单位是分钟。
Session持久化及启动初始化
这个功能主要是,当tomcat执行安全退出时(通过执行shutdown脚本),会将session持久化到本地文件,通常在tomcat的部 署目录下有个session.ser文件。当启动tomcat时,会从这个文件读入session,并添加到manager的session池中去。
这样,当tomcat正常重启时, session没有丢失,对于用户而言,体会不到重启,不影响用户体验。
看一下概念图吧,觉得不是重要实现逻辑,代码就不说了。

Session持久化可以实现当tomcat重新启动后,当前IE使用的session仍然有效而不用重新登录,有两步需要做,session持久 化很有用,尤其在eclipse中重新增加类后,tomcat重新加载后,IE页面不用再登录,之前的session依旧保持,调试的时候很有用
1.配置conf/server.xml
在server.xml的根路径或虚拟目录中增加一段,如虚拟目录调度所中:
<Context path="/dds" docBase="D:/01_XZY/98_供电局调度所/02_JSP/HRDGDZC/ROOT" debug="0" reloadable="true" >
<Manager className="org.apache.catalina.session.PersistentManager" debug="0" saveOnRestart="true" maxActiveSessions="-1" minIdleSwap="-1" maxIdleSwap="-1" maxIdleBackup="-1" >
<Store className="org.apache.catalina.session.FileStore" directory="seskep"/>
</Manager>
</Context>
这样之后,普通的session对象(像字符串类的)就能实现持久化了,但如果session使用了对象,则必须实现对象类的可序列化
参数说明:saveOnRestart-服务器关闭时,是否将所有的session保存到文件中;
maxActiveSessions-可处于活动状态的session数;
minIdleSwap/maxIdleSwap-session处于不活动状态最短/长时间(s),sesson对象转移到File Store中;
maxIdleBackup-超过这一时间,将session备份。(-1表示没有限制)
directory="seskep"  会在tomcat的work目录下建立一个目录,用来存储session,这里建立的目录是
D:/JAVA/TOMCAT4/work/Standalone/localhost/dds/seskep

2.类的序列化
如果session中存储了类xzy登录属性的实例,则类xzy登录属性必须能够序列化,才能实现session持久化
实现implements java.io.Serializable接口就可以了

public class xzy登录属性 implements java.io.Serializable
{
  public String UserName=null;
  public String 姓名=null;
  public String 单位=null;
  public String 部门=null;
  public String 职务=null;
  public String 权限设置=null;
  static final public long serialVersionUID=372938;
}
serialVersionUID这个东西估计每个类中写个不同的数值就可以了,好像是版本保持的.
经过测试IE登录后页面后,Session再重新启动,刷新IE页面的时候session对象中的值能够像重启前一样的读出来.


总结
由此可以看出,session的管理是容器层做的事情,应用层一般不会参与session的管理,也就是说,如果在应用层获取到相应的 session,已经是由tomcat提供的,因此如果过多的依赖session机制来进行一些操作,例如访问控制,安全登录等就不是十分的安全,因为如 果有人能得到正在使用的jsessionid,则就可以侵入系统。

分享到:
评论

相关推荐

    nginx+tomcat+memcached服务架构实现session共享所需jar包

    4. **Session共享**:在“nginx+tomcat+memcached”架构中,实现session共享的关键在于将Tomcat的session管理机制与Memcached相结合。这通常需要引入特定的jar包,例如`memcached-session-manager`,它提供了Tomcat...

    tomcat集群实现session复制

    在IT领域,特别是Web应用服务器的管理与优化中,Tomcat集群实现Session复制是一个关键的技术点,它确保了高可用性和负载均衡,特别是在处理大量并发请求的场景下。本文将深入探讨这一主题,涵盖其原理、配置方法以及...

    Tomcat架构解析文档.zip

    【Java面试必备】Tomcat架构解析 Tomcat作为一款开源的、基于Java的Web应用服务器,是许多初学者和开发者入门Java Web应用的首选。它轻量级、高效且易于管理,广泛应用于小型到中型企业级应用。在面试中,对Tomcat...

    tomcat redis session.rar

    综上所述,"Tomcat Redis Session"是一种在分布式环境中保证用户会话一致性的解决方案,通过集成Redis和Nginx,有效地解决了Web应用集群中的Session管理难题。在实际部署中,还需要考虑系统整体架构、性能优化和运维...

    tomcat架构的源码分析

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

    tomcat 架构解析和优化。pdf

    《Tomcat架构解析与优化》一书主要涵盖了Apache Tomcat服务器的基础架构、工作原理以及性能调优策略。Tomcat作为一款广泛使用的Java Servlet容器,它的高效运行和优化对于任何Java Web应用都至关重要。 首先,...

    springsession管理多台tomcatsession

    在本场景中,我们将讨论如何利用 SpringSession 将 Tomcat 的 session 数据同步到 Redis 数据库存储,以实现高可用性和可扩展性的架构。 首先,理解为什么要使用 SpringSession 和 Redis。在传统的 web 应用中,...

    tomcat7+redis单点 session共享jar

    该资源"tomcat7+redis单点 session共享jar"包含了一个预编译的Java Archive (JAR) 文件,该文件实现了将Tomcat7的session管理与Redis集成的功能。具体来说,这个JAR可能包含以下组件: 1. **Session Store**:一个...

    tomcat 架构分析(概览)

    以下是关于Tomcat架构的详细分析。 首先,核心架构模块是Tomcat的基础,包括Server、Service、Engine、Host和Context以及Wrapper等。Server作为最顶层的组件,包含了多个Service。Service是Tomcat对外提供服务的...

    tomcat集群session共享解决方案

    6. **Cookie管理**:可以将session ID存储在cookie中,通过负载均衡器将请求路由到拥有相应session的服务器。这种方法简化了session的处理,但可能面临cookie篡改或跨站请求伪造(CSRF)攻击的风险。 在实际应用中...

    nginx tomcat memcached 集群 session共享

    这里我们讨论的主题是如何通过Nginx、Tomcat和Memcached来实现集群环境下的session共享。Nginx作为反向代理服务器,负责负载均衡,Tomcat作为应用服务器运行Java Web应用程序,而Memcached则作为分布式内存缓存系统...

    tomcat-redis-session-manager jar包

    【标题】"tomcat-redis-session-manager jar包"是一个用于集成Redis进行Session管理的Java库,特别设计用于Apache Tomcat服务器。这个库使得在多台Tomcat服务器之间共享和协调用户的Session数据成为可能,从而提高了...

    tomcat7/tomcat8+redis 实现session共享

    传统的Web服务器,如Tomcat,通常会在单个服务器上管理用户的Session数据,这在单体应用架构中没有问题。但随着微服务和集群部署的普及,多个服务器之间需要共享Session以确保用户状态的一致性。"tomcat7/tomcat8+...

    nginx+tomcat shiro实现多tomcat下session共享

    "nginx+tomcat shiro实现多tomcat下session共享"这一主题,就是探讨在这样的架构中,如何有效管理和共享session。 1. **Nginx的作用**: Nginx是一个高性能的HTTP和反向代理服务器,常用于负载均衡,可以将用户的...

    tomcat-redis-session-manager tomcat+nginx+redis集群所需jar

    这个"tomcat-redis-session-manager tomcat+nginx+redis集群所需jar"压缩包提供了在这样的架构中实现会话共享所需的Java类库。下面将详细解释其中涉及的技术点: 1. **Apache Tomcat**: Apache Tomcat是一款开源的...

    tomcat-session-manager环境构建资源

    描述中的“Nginx+Tomcat+Session 高性能群集搭建”指出我们正在构建一个使用Nginx和Tomcat的高性能Web服务架构,并且特别关注了Session的管理。Nginx的反向代理功能可以将用户的请求分发到后端的Tomcat实例,而...

    Tomcat、Redis session同步所需jar

    3. **tomcat-redis-session-manager-1.1.jar**:这是一个专门为Tomcat设计的session管理器,它实现了Servlet API中的`HttpSessionManager`接口,使得Tomcat能够将session数据存储到Redis中而不是默认的内存或硬盘上...

    nginx+tomcat+redis完成session共享

    这个小例子"nginx+tomcat+redis完成session共享"旨在演示如何通过这三种技术实现跨服务器的用户会话共享,以提升用户体验并简化系统管理。以下是这些技术及其在会话共享中的作用: 1. **Nginx**:Nginx是一款高性能...

    tomcat 做session共享所需jar包压缩包

    3. **memcached-session-manager-1.6.3.jar**:这个库是专门用于Tomcat的,它提供了一个Session管理器,支持将Session数据存储到Memcached中,实现Session的分布式共享。 4. **kryo-1.04.jar** 和 **kryo-...

    tomcat9+tomcat-cluster-redis-session-manager_4.0.zip

    "tomcat9+tomcat-cluster-redis-session-manager_4.0.zip"这个文件组合涉及到的是在Tomcat 9上实现负载均衡以及使用Redis作为Session管理器的高级配置。 首先,Tomcat 9是Apache Tomcat服务器的一个版本,它是Java ...

Global site tag (gtag.js) - Google Analytics