一道题目引起的思考:“tomcat里怎样禁止服务端自己主动创建session”。

1背景知识:
要说tomcat的机制。先从session说起。
http是无状态协议(http详细可參考:http://www.bysocket.com/?p=282),每次请求都是独立的线程。所以为了维护上下文信息,追踪同一个用户,就是session:保持用户会话状态。眼下有几种方式:cookie,URL重写。隐藏表单域。
Session代表着server和client一次会话的过程。直到session失效(服务端关闭),或者client关闭时结束。Session 是存储在服务端的。并针对每一个client(客户),通过SessionID来差别不同用户的。

Session是以Cookie技术或URL重写实现。默认以Cookie技术实现,服务端会给这次会话创造一个JSESSIONID的值。这个id值会发送给client,client每次请求都会把这个id值放到http请求的头部发送给服务端,而这个id值在client会保存下来,保存的容器就是cookie。

2. tomcat 的session管理机制:

2.1 请求过程中session操作
简述:在请求过程中首先要解析请求中的sessionId信息,然后将sessionId存储到request的參数列表中。然后再从request获取session的时候,假设存在sessionId那么就依据Id从session池中获取session,假设sessionId不存在或者session失效,那么则新建session而且将session信息放入session池。供下次使用。
我们使用session一般这样使用:request.getSession().能够看下
  1.  
  2. 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
    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) {
    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
    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()) {
    String scName = context.getSessionCookieName();
    if (scName == null) {
    scName = Globals.SESSION_COOKIE_NAME;
    }
    Cookie cookie = new Cookie(scName, session.getIdInternal());
    configureSessionCookie(cookie);
    response.addSessionCookieInternal(cookie, context.getUseHttpOnly());
    }

    if (session != null) {
    session.access();
    return (session);
    } else {
    return (null);
    }
    }

上面代码就是依据requestedSessionId 去查找session,找不到就创建新的session。那么requestedSessionId 怎么获取的呢?

Http11Processor解析封装在org.apache.coyote.Request然后传递给CoyoteAdapter。coyoteAdapter是一个适配器,将coyote框架封装的org.apache.coyote.Request适配给org.apache.catalina.connector.Request。过程就是Http11Processor方法process()。里面调用了 adapter.service(request, response);coyoteAdapter的service里面postParseRequest(req, request, res, response).当中调用了  parsePathParameters(req, request)方法去解析路径參数中的cookie信息,parseSessionCookiesId(req, request)从cookie中解析sessionId存到request;
上面的Http11Processor是怎么调用的.还没查到。待补充。
2.2 Servlet获取session的流程:

appServlet是我们自己定义的一个Servlet,在通过Reqest获取session的时候,事实上调用的这个先看javax.servlet.http.HttpServletRequest.getSession(boolean create)(这是一个接口)事实上是RequestFacade(封装了org.apache.catalina.connector.Request的一个门面),然后RequestFacade会调用真实的Request的getSession方法。

Request详细的逻辑是调用Context容器的getManger方法获取Session管理器(session管理器详情下文介绍),然后假设SessionId假设已经被解析出来了。那么则会调用findSession方法从session对象池中获取相应的session,反之假设sessionId不存在,则须要又一次创建一个Session,并放入session对象池中。

以下贴出关键代码:

类RequestFacade的getSession方法:

类Request的getSession方法:
org.apache.catalina.connector
doGetSession就是上面2.1贴出来的代码,主要逻辑就是一依据SessionId从session对象池中获取session信息: session = manager.findSession(requestedSessionId) 用于查找sessionid相应的session;,第二标记就是在没有解析到sessionId的情况下创建一个新的Session对象。
类ManagerBase的findSession方法:
 

注意:  protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();

也就是说tomcat是通过线程安全的ConcurrentHashMap来实现的。


以上即是Servlet获取session的流程。
2.3.Session的管理机制
以下是tomcat6源代码里面session包:

  实现包的路径是:org.apache.catalina.session,tomcat对外提供session调用的接口不在这个实现包里。对外接口是在包javax.servlet.http下的HttpSession。而实现包里的StandardSession是tomcat提供的标准实现,当然对外tomcat不希望用户直接操作StandardSession,而是提供了一个StandardSessionFacade类,tomcat容器里详细操作session的组件是servlet。而servlet操作session是通过StandardSessionFacade进行的。这样就能够防止程序猿直接操作StandardSession所带来的安全问题。

session包下各类含义:

(1)    Manager:定义了关联到某一个容器的用来管理session池的基本接口。这是catalina包下的。

(2)    ManagerBase:实现了Manager接口。该类提供了Session管理器的常见功能的实现。

(3)    StandardManager:继承自ManagerBase,tomcat的默认Session管理器(不指定配置,默认使用这个),是tomcat处理session的非集群实现(也就说是单机版的),tomcat关闭时,内存session信息会持久化到磁盘保存为SESSION.ser,再次启动时恢复。

(4)    PersistentManagerBase:继承自ManagerBase,实现了和定义了session管理器持久化的基础功能。

(5)    PersistentManager:继承自PersistentManagerBase,主要实现的功能是会把空暇的会话对象(通过设定超时时间)交换到磁盘上。

(6)StoreBase:存放持久化有关store接口的基本实现。

持久化session管理器的存储策略就是有这个Store对象定义的。

接口Store及事实上例是为session管理器提供了一套存储策略。store定义了主要的接口。而StoreBase提供了主要的实现。

当中FileStore类实现的策略是将session存储在以setDirectory()指定文件夹并以.session结尾的文件里的。JDBCStore类是将Session通过JDBC存入数据库中,因此须要使用JDBCStore,须要分别调用setDriverName()方法和setConnectionURL()方法来设置驱动程序名称和连接URL。

(7) StandardSession :tomcat的session实现类。

2.4 Tomcat session相关的配置

从两个层面总结一下session相关的配置和设置。首先是从配置文件层面,session是有过期时间的,这个默认的过期时间是 在$catalina_home/conf/web.xml有定义的。详细的默认配置例如以下(默认的过期时间是30min,即30min没有訪 问,session就过期了):

另一点就是session管理假设不配置就默认使用StandardManager。但假设要配置的话能够 在$catalina_home/conf/context.xml当中指定(当中从这个配置当中能够看到session管理器是和context容器关 联的,也就说每一个web应用都会有一个session管理器)详细的配置例如以下:

Tomcat7.x默认这个manager的配置是凝视掉的。假设要指定的PersistentManager为默认管理器的话能够这么指定:

事实上看到这也就发现了,事实上session管理器或者Store存储策略。仅仅要实现了相关的接口,都是能够自己定义的。

自己写一个配置在这里就ok了。

另外在从代码层面总结一下:session的一些配置信息是写死在代码里的,比方SessionConfig这个类就定义了一些session的设 置信息。Session在cookie中的名字是JSESSION. Session通过URL重写的方式放在path里时,键值的名字是jsessionids,详细的代码例如以下:

另一点就是sessionId默认指定的长度是16个字节,这个在SessionIdGenerator当中指定:

这个 SessionIdGenerator应该是tomcat7的。我在6上源代码找不到。

关于tomcat6的源代码里面。有这种方法generateSessionId:

  1.  
  2. protected synchronized String generateSessionId() {

    byte random[] = new byte[16];
    String jvmRoute = getJvmRoute();
    String result = null;

    // Render the result as a String of hexadecimal digits
    StringBuffer buffer = new StringBuffer();
    do {
    int resultLenBytes = 0;
    if (result != null) {
    buffer = new StringBuffer();
    duplicates++;
    }

    while (resultLenBytes < this.sessionIdLength) {
    getRandomBytes(random);
    random = getDigest().digest(random);
    for (int j = 0;
    j < random.length && resultLenBytes < this.sessionIdLength;
    j++) {
    byte b1 = (byte) ((random[j] & 0xf0) >> 4);
    byte b2 = (byte) (random[j] & 0x0f);
    if (b1 < 10)
    buffer.append((char) ('0' + b1));
    else
    buffer.append((char) ('A' + (b1 - 10)));
    if (b2 < 10)
    buffer.append((char) ('0' + b2));
    else
    buffer.append((char) ('A' + (b2 - 10)));
    resultLenBytes++;
    }
    }
    if (jvmRoute != null) {
    buffer.append('.').append(jvmRoute);
    }
    result = buffer.toString();
    } while (sessions.containsKey(result));
    return (result);

    }

实现原理:一个随机数加时间加上jvm的id值,jvm的id值会依据server的硬件信息计算得来,因此不同jvm的id值都是唯一的

再补充一点:session本身也是基于kv的hashmap实现的,比方:StandardSession 里面

protected Map attributes = new ConcurrentHashMap();

全部的会话信息的存取都是通过这个属性来实现的。另外上面web.xml的配置有效期tomcat是怎么实现的呢?

就是ManagerBase.processExpires。一直查询全部的session对象。每一个检查是否有效,超期就删除。

详细代码參见

当中session在expire会触发相应的listener事件,从而对session信息进行监控,以下是expire方法部分截图。


**********************总结*********************

tomcat的session管理机制基本上总结就是上面。这个源代码分析师基于tomcat6的。7还是有一些差异跟优化的。

回答问题:假设不想让session被创建,则自己定义ManagerBase的实现类。覆盖这些方法为空实现。
思考:

在实际运用中session所带来的问题

由上面所描写叙述的session实现机制。我们会发现。为了弥补http协议的无状态的特点。服务端会占用一定的内存和cpu用来存储和处理session计算的开销,这也就是tomcat这个的web容器的并发连接那么低(tomcat官方文档里默认的连接数是200)原因之中的一个。因此非常多java语言编写的站点。在生产环境里web容器之前会加一个静态资源server,比如:apacheserver或nginxserver。静态资源server没有解决http无状态问题的功能,因此部署静态资源的server也就不会让出内存或cpu计算资源专门去处理像session这种功能。这些内存和cpu资源能够更有效的处理每一个http请求,因此静态资源server的并发连接数更高,所以我们能够让那些没有状态保持要求的请求直接在静态server里处理。而要进行状态保持的请求则在java的web容器里进行处理。这样能更好的提升站点的效率

解决session相关问题的技术方案

由上所述,session一共同拥有两个问题须要解决:

1) session的存储应该独立于web容器。也要独立于部署web容器的server。

2)怎样进行高效的session同步。

因此最好的解决方式就是使用分布式缓存技术,比如:memcached和redis。将session信息的存储独立出来也是解决session同步问题的方法。

以下是一篇用zookeeper实现的分布式session方案:

http://www.open-open.com/lib/view/open1378556537303.html

參考:
http://www.cnblogs.com/sharpxiajun/p/3395607.html
http://blog.csdn.net/it_man/article/details/26217143
http://www.cnblogs.com/interdrp/p/4935614.html 

关于tomcat session机制梳理的更多相关文章

  1. Session会话保持机制的原理与Tomcat Session共享的几种实现方式(Session Cluster、memcached+MSM)

    一.Session的定义 在计算机科学中,特别是在网络中,session是两个或更多个通信设备之间或计算机和用户之间的临时和交互式信息交换.session在某个时间点建立,然后在之后的某一时间点拆除. ...

  2. Tomcat源码分析 (十)----- 彻底理解 Session机制

    Tomcat Session 概述 首先 HTTP 是一个无状态的协议, 这意味着每次发起的HTTP请求, 都是一个全新的请求(与上个请求没有任何联系, 服务端不会保留上个请求的任何信息), 而 Se ...

  3. session机制详解以及session的相关应用

    session是web开发里一个重要的概念,在大多数web应用里session都是被当做现成的东西,拿来就直接用,但是一些复杂的web应用里能拿来用的session已经满足不了实际的需求,当碰到这样的 ...

  4. Cookie/Session机制详解

    会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端 ...

  5. 理解Cookie和Session机制(转)

    目录[-] Cookie机制 什么是Cookie 记录用户访问次数 Cookie的不可跨域名性 Unicode编码:保存中文 BASE64编码:保存二进制图片 设置Cookie的所有属性 Cookie ...

  6. [转]Cookie/Session机制详解

    原文地址:http://blog.csdn.net/fangaoxin/article/details/6952954 会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用 ...

  7. 关于cookie的文章(cookie与session机制)

    会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端 ...

  8. Session机制详解

    转自:http://justsee.iteye.com/blog/1570652 虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能 ...

  9. 理解Cookie和Session机制

    转载: 理解Cookie和Session机制 会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录 ...

随机推荐

  1. Laravel View Composer - 当 include 一个模板时,自动获取其所需的变量

    网站中,许多页面的侧边栏是相同的.例如: 分类列表页,与文章详情页的侧边栏都包含 最新文章 最新评论 统计计数 这些相同的侧边栏数据也是动态的,并不是固定的. 在每个 controller 里都写一遍 ...

  2. hdu3015树状数组 poj1990的离散化版本

    都是一类题目,推导调试比较烦,想出来还是不难的 /* 给定n个点对,按一维升序排序一次,每个点的序号为Di,按二维升序排序一次,每个点的序号为Hi 求sum{w(i,j)} w(i,j)=abs(Di ...

  3. bzoj1208splay模板题

    想试下新找的板子,没想到交上去CE了..懒得调..以后有机会就改 /* 用type标记当前树上的是宠物还是人 每次求前驱后缀,删掉最近的那个点 */ #include<iostream> ...

  4. Android Monkey压力测试环境搭建及使用

    Android Monkey压力测试学习笔记 步骤:下载SDK -> 解压进入SDK Manager下载系统 -> 配置环境变量 -> 创建虚拟设备或连接真机 -> 进入命令模 ...

  5. python 全栈开发,Day68(Django的路由控制)

    昨日内容回顾 1 MVC和MTV MTV 路由控制层(分发哪一个路径由哪一个视图函数处理) V : views (逻辑处理) T : templates (存放html文件) M : model (与 ...

  6. HDU 1029 某个数出现的次数大于等于(N+1)/2的是哪个 map水题

    题意:输入n个数 n为奇数 问某个数出现的次数大于等于(N+1)/2的是 哪个 输出来Sample Input51 3 2 3 3111 1 1 1 1 5 5 5 5 5 571 1 1 1 1 1 ...

  7. asp.net core 支付宝支付( 电脑2.0)

    支付宝电脑支付实测在手机浏览器也可以唤醒手机支付宝进行支付,因此也可以作为支付宝手机web支付方式.支付宝电脑支付流程为使用支付宝官方sdk通过获取的支付宝参数构造DefaultAopClient实例 ...

  8. BZOJ1821 [JSOI2010]Group 部落划分 Group Kruskal

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1821 题意概括 平面上有n个点,现在把他们划分成k个部分,求不同部分之间最近距离的最大值. 两个部 ...

  9. 【转】TCP三次握手和四次挥手全过程及为什么要三次握手解答

    TCP三次握手和四次挥手的全过程   TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接: 位码即tcp标志位,有6种表示: SYN(synchronous建立连接) ...

  10. P2789 直线交点数

    P2789 直线交点数分成两种情况,一种是平行直线,一种是自由直线,在自由直线中可以存在平行直线,但是不能和第一组的直线平行.自由直线和平行直线的交点是i*(n-i). #include<ios ...