本系列转载自 http://blog.csdn.net/haitao111313/article/category/1179996

在明白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方法处理,在此方法有这么几句:
[java] view plaincopyprint?
  1. parseConnection(socket);
  2. parseRequest(input, output);//解析请求行,如果有jessionid,会在方法里面解析jessionid
  3. if (!request.getRequest().getProtocol()
  4. .startsWith("HTTP/0"))
  5. parseHeaders(input);//解析请求头部,如果有cookie字段,在方法里面会解析cookie,

下面看parseRequest方法里面是怎么解析jessionid的,这种解析方式是针对url重写的:

[java] view plaincopyprint?
  1. parseRequest方法:
  2. int semicolon = uri.indexOf(match);//match是“;JSESSIONID=”,即在请求行查找字段JSESSIONID
  3. if (semicolon >= 0) {                                   //如果有JSESSIONID字段,表示不是第一次访问
  4. String rest = uri.substring(semicolon + match.length());
  5. int semicolon2 = rest.indexOf(';');
  6. if (semicolon2 >= 0) {
  7. request.setRequestedSessionId(rest.substring(0, semicolon2));//设置sessionid
  8. rest = rest.substring(semicolon2);
  9. } else {
  10. request.setRequestedSessionId(rest);
  11. rest = "";
  12. }
  13. request.setRequestedSessionURL(true);
  14. uri = uri.substring(0, semicolon) + rest;
  15. if (debug >= 1)
  16. log(" Requested URL session id is " +
  17. ((HttpServletRequest) request.getRequest())
  18. .getRequestedSessionId());
  19. } else {                               //如果请求行没有JSESSIONID字段,表示是第一次访问。
  20. request.setRequestedSessionId(null);
  21. request.setRequestedSessionURL(false);
  22. }

代码没什么说的,看url有没有JSESSIONID,有就设置request的sessionid,没有就设置为null。有再看parseHeaders方法:

[java] view plaincopyprint?
  1. parseHeaders方法:
  2. .....
  3. ....else if (header.equals(DefaultHeaders.COOKIE_NAME)) { //COOKIE_NAME的值是cookie
  4. Cookie cookies[] = RequestUtil.parseCookieHeader(value);
  5. for (int i = 0; i < cookies.length; i++) {
  6. if (cookies[i].getName().equals
  7. (Globals.SESSION_COOKIE_NAME)) {
  8. // Override anything requested in the URL
  9. if (!request.isRequestedSessionIdFromCookie()) {
  10. // Accept only the first session id cookie
  11. request.setRequestedSessionId
  12. (cookies[i].getValue());//设置sessionid
  13. request.setRequestedSessionCookie(true);
  14. request.setRequestedSessionURL(false);
  15. if (debug >= 1)
  16. log(" Requested cookie session id is " +
  17. ((HttpServletRequest) request.getRequest())
  18. .getRequestedSessionId());
  19. }
  20. }
  21. if (debug >= 1)
  22. log(" Adding cookie " + cookies[i].getName() + "=" +
  23. cookies[i].getValue());
  24. request.addCookie(cookies[i]);
  25. }
  26. }

代码主要就是从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有这两个方法:
[java] view plaincopyprint?
  1. ManagerBase类的findSession和createSession()方法
  2. public Session findSession(String id) throws IOException {
  3. if (id == null)
  4. return (null);
  5. synchronized (sessions) {
  6. Session session = (Session) sessions.get(id);//根据sessionid(即<span style="font-family: Arial; ">JSESSIONID</span>)查找session对象。
  7. return (session);
  8. }
  9. }
  10. public Session createSession() { //创建session对象
  11. // Recycle or create a Session instance
  12. Session session = null;
  13. synchronized (recycled) {
  14. int size = recycled.size();
  15. if (size > 0) {
  16. session = (Session) recycled.get(size - 1);
  17. recycled.remove(size - 1);
  18. }
  19. }
  20. if (session != null)
  21. session.setManager(this);
  22. else
  23. session = new StandardSession(this);
  24. // Initialize the properties of the new session and return it
  25. session.setNew(true);
  26. session.setValid(true);
  27. session.setCreationTime(System.currentTimeMillis());
  28. session.setMaxInactiveInterval(this.maxInactiveInterval);
  29. String sessionId = generateSessionId();//使用md5算法生成sessionId
  30. String jvmRoute = getJvmRoute();
  31. // @todo Move appending of jvmRoute generateSessionId()???
  32. if (jvmRoute != null) {
  33. sessionId += '.' + jvmRoute;
  34. session.setId(sessionId);
  35. }
  36. session.setId(sessionId);
  37. return (session);
  38. }

以上是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()方法:

[java] view plaincopyprint?
  1. HttpRequestBase.getSession()
  2. 调用---------------》
  3. HttpRequestBase.getSession(boolean create)
  4. 调用 ----------------》
  5. HttpRequestBase.doGetSession(boolean create){
  6. if (context == null)
  7. return (null);
  8. // Return the current session if it exists and is valid
  9. if ((session != null) && !session.isValid())
  10. session = null;
  11. if (session != null)
  12. return (session.getSession());
  13. // Return the requested session if it exists and is valid
  14. Manager manager = null;
  15. if (context != null)
  16. manager = context.getManager();
  17. if (manager == null)
  18. return (null);      // Sessions are not supported
  19. if (requestedSessionId != null) {
  20. try {
  21. session = manager.findSession(requestedSessionId);//这里调用StandardManager的findSession方法查找是否存在Session对象
  22. } catch (IOException e) {
  23. session = null;
  24. }
  25. if ((session != null) && !session.isValid())
  26. session = null;
  27. if (session != null) {
  28. return (session.getSession());
  29. }
  30. }
  31. // Create a new session if requested and the response is not committed
  32. if (!create)
  33. return (null);
  34. if ((context != null) && (response != null) &&
  35. context.getCookies() &&
  36. response.getResponse().isCommitted()) {
  37. throw new IllegalStateException
  38. (sm.getString("httpRequestBase.createCommitted"));
  39. }
  40. session = manager.createSession();//这里调用StandardManager的创建Session对象
  41. if (session != null)
  42. return (session.getSession());
  43. else
  44. return (null);
  45. }

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

Tomcat源码分析(九)--Session管理相关推荐

  1. tomcat源码阅读之session管理器(Manager)

    一.UML图分析: (一) Session: Session保存了一个客户端访问服务器时,服务器专门为这个客户端建立一个session用来保存相关的会话信息,session有一个有效时间,这个时间默认 ...

  2. Tomcat源码分析--转

    一.架构 下面谈谈我对Tomcat架构的理解 总体架构: 1.面向组件架构 2.基于JMX 3.事件侦听 1)面向组件架构 tomcat代码看似很庞大,但从结构上看却很清晰和简单,它主要由一堆组件组成 ...

  3. 云客Drupal源码分析之Session进阶

    在本系列之前写过<云客Drupal源码分析之Session系统>,但那部分仅仅讲到了drupal会话的基础:Symfony的Session组件 至于drupal怎么去使用这个基础就是本主题 ...

  4. Tomcat源码分析

    Tomcat源码分析 ` 最近深入了解了下tomcat的源码,在此记录下 文章目录 Tomcat源码分析 前言 一.Tomcat整体架构是什么? 1)分析配置文件server.xml, 2)网上盗个图 ...

  5. Tomcat源码分析(十)--部署器 转载

    本系列转载自 http://blog.csdn.net/haitao111313/article/category/1179996 我们知道,在Tomcat的世界里,一个Host容器代表一个虚机器资源 ...

  6. RocketMQ4.0源码分析之-路由管理

    RocketMQ4.0源码分析之-路由管理 一 前言 路由管理功能是RocketMQ的核心功能之一,涵盖了订阅管理,连接管理,负载均衡管理等一系列功能,代码布在NameServer,Broker,Pr ...

  7. caffe源码分析--SyncedMemory 内存管理机制

    caffe源码分析–SyncedMemory 内存管理机制 ​ SyncedMemory 是caffe中用来管理内存分配和CPU.GPU数据及同步的类,只服务于Blob类.SyncedMemory 对 ...

  8. Linux内核源码分析《进程管理》

    Linux内核源码分析<进程管理> 前言 1. Linux 内核源码分析架构 2. 进程原理分析 2.1 进程基础知识 2.2 Linux进程四要素 2.3 进程描述符 task_stru ...

  9. v05.05 鸿蒙内核源码分析(任务管理) | 如何管理任务池 | 百篇博客分析HarmonyOS源码

    曾子曰:"吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?"<论语>:学而篇 百篇博客系列篇.本篇为: v05.xx 鸿蒙内核源码分析(任务管理篇) | 如何 ...

最新文章

  1. 计算机网络实验五,计算机网络(实验五).docx
  2. QT的QScriptEngineAgent类的使用
  3. 开源项目SlidingMenu的使用(Android)
  4. 【VMCloud云平台】SCSM(六)SCSM创建服务
  5. [转]计算机四级网络工程师思维导图--计算机网络部分
  6. JQuery版本下载链接
  7. 蓝桥杯练习------python字符串逆序
  8. 忍之の爱你术 全代码
  9. 如何发现适合自己深度投入的领域?
  10. Burp Spider 使用指南
  11. 【交易架构day4】京东到家交易系统的演进之路
  12. 如何能快速看懂一个Java项目?
  13. FIFO,LPU,CLOCK时钟算法
  14. leetcode第六题 Z字形变换
  15. pcx游程编码、解码超详细讲解(附带java源码)
  16. 微软 Windows 7的“杀手锏”是智能
  17. 英伟达 (Nvidia) GPU 系统管理界面(SMI)
  18. Linux 上如何让任意普通用户执行拥有root权限的特定脚本或者程序
  19. 苹果CMS采集资源站
  20. 阿里斥资百亿参建“中国神网”,5G发展更进一步

热门文章

  1. sniffer 工具
  2. 3/100. Merge Two Binary Trees
  3. 2/100. Hamming Distance
  4. Drupal 覆写系统样式
  5. Drupal 使用 Views 模块时,提示 Requires: Ctools (missing) 解决办法
  6. WPF中引入外部资源
  7. sendEmail实现邮件报警
  8. UML分析AsyncDisplayKit框架-ASMuplexImageNode异步下载时序图。
  9. Swift中文教程(三)--流程控制
  10. IT项目管理的十六个字心得体会