关于tomcat session机制梳理
Session是以Cookie技术或URL重写实现。默认以Cookie技术实现,服务端会给这次会话创造一个JSESSIONID的值。这个id值会发送给client,client每次请求都会把这个id值放到http请求的头部发送给服务端,而这个id值在client会保存下来,保存的容器就是cookie。
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 怎么获取的呢?
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方法:
注意: protected Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
实现包的路径是:org.apache.catalina.session,tomcat对外提供session调用的接口不在这个实现包里。对外接口是在包javax.servlet.http下的HttpSession。而实现包里的StandardSession是tomcat提供的标准实现,当然对外tomcat不希望用户直接操作StandardSession,而是提供了一个StandardSessionFacade类,tomcat容器里详细操作session的组件是servlet。而servlet操作session是通过StandardSessionFacade进行的。这样就能够防止程序猿直接操作StandardSession所带来的安全问题。
(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接口的基本实现。
接口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:
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方法部分截图。
**********************总结*********************
在实际运用中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同步。
以下是一篇用zookeeper实现的分布式session方案:
关于tomcat session机制梳理的更多相关文章
- Session会话保持机制的原理与Tomcat Session共享的几种实现方式(Session Cluster、memcached+MSM)
一.Session的定义 在计算机科学中,特别是在网络中,session是两个或更多个通信设备之间或计算机和用户之间的临时和交互式信息交换.session在某个时间点建立,然后在之后的某一时间点拆除. ...
- Tomcat源码分析 (十)----- 彻底理解 Session机制
Tomcat Session 概述 首先 HTTP 是一个无状态的协议, 这意味着每次发起的HTTP请求, 都是一个全新的请求(与上个请求没有任何联系, 服务端不会保留上个请求的任何信息), 而 Se ...
- session机制详解以及session的相关应用
session是web开发里一个重要的概念,在大多数web应用里session都是被当做现成的东西,拿来就直接用,但是一些复杂的web应用里能拿来用的session已经满足不了实际的需求,当碰到这样的 ...
- Cookie/Session机制详解
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端 ...
- 理解Cookie和Session机制(转)
目录[-] Cookie机制 什么是Cookie 记录用户访问次数 Cookie的不可跨域名性 Unicode编码:保存中文 BASE64编码:保存二进制图片 设置Cookie的所有属性 Cookie ...
- [转]Cookie/Session机制详解
原文地址:http://blog.csdn.net/fangaoxin/article/details/6952954 会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用 ...
- 关于cookie的文章(cookie与session机制)
会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端 ...
- Session机制详解
转自:http://justsee.iteye.com/blog/1570652 虽然session机制在web应用程序中被采用已经很长时间了,但是仍然有很多人不清楚session机制的本质,以至不能 ...
- 理解Cookie和Session机制
转载: 理解Cookie和Session机制 会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话.常用的会话跟踪技术是Cookie与Session.Cookie通过在客户端记录 ...
随机推荐
- 使用RabbitMQ实现延迟任务
场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时. 场景二:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单. 上述类似的需求是我们经常会遇见的问题. ...
- thinkphp错误提示:系统发生错误
下载最新版本3.1.3,定义了一个应用,进入应用的config.php,在里面添加数据库类链接信息,在控制器里面M()一个表,访问控制器方法提示:系统发生错误.如果使用连接字符串DSN方式,调用M() ...
- Tomcat下指定JDK
- GItlab作CI/CD时,想快点,有啥招?
如果希望.m2文件有存缓,或是不要每次从dockerhub上找镜像(有的是本地镜像,远程没有的) 那么,gitlab-runner的config.toml初步优化文件如下: concurrent = ...
- 记录weiye项目上线遇到的一些问题
1.使用vpn访问客户内网 参考:http://jingyan.baidu.com/article/a3f121e4f9903cfc9052bb0b.html 2.设置使用ip地址直接访问项目(之后可 ...
- Kubernetes学习系列
这段时间项目组内想要引入Kubernetes,作为第二代容器调度引擎,故最近在系统的学习Kubernetes.整理了一些学习笔记,心得,放到博客中,一来记录自己的学习经过,二来看能否帮到有需要的同学. ...
- 【Java】 剑指offer(22) 链表中倒数第k个结点
正文 本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 输入一个链表,输出该链表中倒数第k个结点.为了符合大多数人的 ...
- Strom在本地运行调试出现的错误
1.错误日志 31385 [main] WARN backtype.storm.daemon.nimbus - Topology submission exception. (topology nam ...
- 015 在Spark中关于groupByKey与reduceByKey的区别
1.groupByKey的源代码 2.groupByKey的使用缺点 不使用groupByKey的主要原因:在大规模的数据下,数据分布不均匀的情况下,可能导致OOM 3.reduceByKey的源代码 ...
- drupal笔记
$app_root :网站根目录 安装 汉化:1将汉化包放置drupal8\sites\default\files\translations下安装:2极简版的话需要在extend(扩展)中安装Inte ...