Tomcat 之session 持久化2
通过前文 Tomcat 之session 持久化1 ,我们已经大概了解了这么个机制。但是我没能详细展开其底层的原理。
这篇文章,我想稍微深入一点点,再继续聊一聊其底层。
Tomcat 之session 持久化的作用:
这样做的好处是:减少系统资源的占用,如果Servlet容器突然关闭或重启,或Web应用重启,这些持久化了的HttpSession对象可以再重新加载进来,对于客户端,还是使用同一个Session。
Tomcat提供了哪些实现?
StandardManager 会在每次关闭tomcat的时候把所有的session 持久化到 SESSIONS.ser, 然后再次启动的时候读取 SESSIONS.ser, 然后删除这个文件。
PersistentManager 呢, 为每一个session 生成一个session文件,或者数据库表的一行记录。如 8B66FF7606964BD4D4D6B3225170CCB2.session。
对于FileStore ,为每个session,创建一个.session 文件。 创建是在适当的时候进行的。 文件以 sessionId + .session 为名, 这点和其他的store 有所不同。
对于JDBCStore, 每个session 会被存储到表的一行,以sessionId为key。 注意,配置JDBCStore 的时候,不要有任何的错误,否则就无法持久化,而且呢,TMD,Tomcat竟然也不报错。比如我配置sessionValidCol这个属性的时候, session_valid 写成了 valid_session, 花了很长时间才发现,坑!
关于持久化的时机:
其实具体什么时候进行持久化, 默认都是在关闭tomcat的时候进行的 ,但具体是 配置的参数相关的:
debug
设定Session Manager采用的跟踪级别,取值0到99,越小越跟踪信息越少,发布产品时,应该设置为0,以提高性能。
saveOnRestart
如果为true,则当Tomcat关闭时,所有的有效HttpSession对象都保存到Session Store中;当Tomcat重启时,加载这些HttpSession对象。
maxActiveSessions
设置处于活动状态的Session的最大数目,如果超过这一数目,Tomcat把一些超过的Sessin对象保存到Session Store中。-1表示不限制。
minIdleSwap
Session处于不活动状态的最小时间,单位为秒,超过这一时间,Tomcat有可能把这个Session对象移到Session Store中。
maxIdleSwap
Session处于不活动状态的最大时间,超过这一时间,Tomcat就一定会将这个Session对象移到Session Store中。
maxIdleBackup
Session处于不活动状态的最大时间,超过这一时间,Tomcat就就会将这个Session对象拷贝到Session Store中进行备份。
directory
指定Session Store在哪个文件系统目录下存放持久化的Session对象的信息,文件名是Session ID.session。
持久化,到底具体持久了什么?
我之前其实有个先入为主的观念是,Tomcat 可以把session , 那么是否就是说我们把浏览器关闭后, tomcat 也关闭了后, 重启,再次访问那个web 应用, 就可以不用登陆的吧!
让我们通过源码仔细看看被持久化的session 具体有哪些内容吧!
观察源码发现,关键代码就在于 StandardSession , 参照其中的readObject 方法, 我写了一个测试类:
package com.lk; import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.WriteAbortedException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; public class SessonDeserialization { public static void main(String[] args) throws Exception { String filePath = "D:\\soft\\apache-tomcat-7.0.69\\work\\Catalina\\localhost" +
// "\\session\\D6C5454611334414893B1EB6E3E966BD.session";
"\\session\\66792850BAEAF9A4BD17877F1A27B551.session"; File file = new File(filePath);
FileInputStream fis = new FileInputStream(file.getAbsolutePath());
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream ois = new ObjectInputStream(bis);
readObject(ois); } protected static LkSession readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException {
// this.authType = null;
long creationTime = ((Long)stream.readObject()).longValue();
long lastAccessedTime = ((Long)stream.readObject()).longValue();
int maxInactiveInterval = ((Integer)stream.readObject()).intValue();
boolean isNew = ((Boolean)stream.readObject()).booleanValue();
boolean isValid = ((Boolean)stream.readObject()).booleanValue();
long thisAccessedTime = ((Long)stream.readObject()).longValue();
Object principal = null;
String id = (String)stream.readObject(); Map attributes = new ConcurrentHashMap(); int n = ((Integer)stream.readObject()).intValue();// session 的个数 System.out.println(n);
boolean isValidSave = isValid;
isValid = true; for(int i = ; i < n; ++i) {
String name = (String)stream.readObject(); Object value;
try {
value = stream.readObject();
} catch (WriteAbortedException var8) {
// if(var8.getCause() instanceof NotSerializableException) {
// continue; // 从这里可以看到, 如果不能序列化, 那么直接忽略
// }
throw var8;
} // if(!this.exclude(name, value)) {
attributes.put(name, value);
// } } isValid = isValidSave; return new LkSession(creationTime, lastAccessedTime, id, maxInactiveInterval, isNew, isValid
,thisAccessedTime, attributes);
} }
可见,基本上很多有用的东西都被持久化了下来。它们按照一定的格式被组织了起来:
creationTime = [1509271063483], lastAccessedTime = [1509271929705], id = [66792850BAEAF9A4BD17877F1A27B551], maxInactiveInterval = [1800], isNew = [false], isValid = [true], thisAccessedTime = [1509271929705], attributes = [{aaa=AAAAAA, user=User [username=aa, password=bb], ccc=how are you ! 你好啊 ! , bb=111}]
而 writeObject 其实也很好理解,就是把可以序列化的内容序列化起来!:
protected void writeObject(ObjectOutputStream stream) throws IOException {
stream.writeObject(Long.valueOf(this.creationTime));
stream.writeObject(Long.valueOf(this.lastAccessedTime));
stream.writeObject(Integer.valueOf(this.maxInactiveInterval));
stream.writeObject(Boolean.valueOf(this.isNew));
stream.writeObject(Boolean.valueOf(this.isValid));
stream.writeObject(Long.valueOf(this.thisAccessedTime));
stream.writeObject(this.id);
if(this.manager.getContainer().getLogger().isDebugEnabled()) {
this.manager.getContainer().getLogger().debug("writeObject() storing session " + this.id);
} String[] keys = this.keys();
ArrayList saveNames = new ArrayList();
ArrayList saveValues = new ArrayList(); int n;
for(n = ; n < keys.length; ++n) {
Object i = this.attributes.get(keys[n]);
if(i != null) {
if(this.isAttributeDistributable(keys[n], i) && !this.exclude(keys[n], i)) {
saveNames.add(keys[n]);
saveValues.add(i);
} else {
this.removeAttributeInternal(keys[n], true);
}
}
} n = saveNames.size();
stream.writeObject(Integer.valueOf(n)); for(int var9 = ; var9 < n; ++var9) {
stream.writeObject(saveNames.get(var9)); try {
stream.writeObject(saveValues.get(var9));
if(this.manager.getContainer().getLogger().isDebugEnabled()) {
this.manager.getContainer().getLogger().debug(" storing attribute \'" + (String)saveNames.get(var9) + "\' with value \'" + saveValues.get(var9) + "\'");
}
} catch (NotSerializableException var8) {
this.manager.getContainer().getLogger().warn(sm.getString("standardSession.notSerializable", new Object[]{saveNames.get(var9), this.id}), var8);
}
} }
简易登录拦截器
鉴于此, 我把我之前的代码有完善了下。 我增加了个 登录拦截器功能:
package com.lk; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; public class SessionAuthInterceptor { public static boolean isLoggedIn(HttpServletRequest req) {
HttpSession session = req.getSession(); String sessionid = session.getId();
// System.out.println(sessionid);
long lastAccessedTime = session.getLastAccessedTime();
System.out.println(lastAccessedTime); User user = (User) session.getAttribute("user");// User必须 implements Serializable, 否则这里获取到的就是 null if (user != null) {
System.out.println("已登陆:"+ sessionid);
return true;
} System.err.println("未登陆:"+ sessionid);
return false;
} }
然后呢,在相关的servlet 代码里面,增加对session的判断, 如此就可以判断当前用户是否已经登录过了:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (!SessionAuthInterceptor.isLoggedIn(req)) { // 这里可以改成用AOP 来实现。
req.getRequestDispatcher("login.jsp").forward(req, resp);
return;
}
...
}
参考:
http://blog.csdn.net/caomiao2006/article/details/51291005
Tomcat 之session 持久化2的更多相关文章
- Tomcat 之session 持久化1
Tomcat 之session 持久化原理 几个概念: Manager 接口,其实就是指的是对 其Sesison 的管理, 其默认实现是StandardManager (内部没有任何Store对象实 ...
- 【Session】Tomcat中Session持久化到文件系统或数据库
参考的优秀文章 Tomcat Session 持久化 Package org.apache.catalina.session 最近同事在做Session外置的功能,我对Session持久化.共享也不太 ...
- Tomcat下 session 持久化问题(重启服务器session 仍然存在)
感谢大佬:https://www.iteye.com/blog/xiaolongfeixiang-560800 关于在线人数统计,大都使用SessionListener监听器实现. SessionLi ...
- 细说tomcat之session持久化探秘
业务场景:通常,我们会在会话级别存放一些参数,期望在session生命周期内,可以一直取得保存在session中的指定数据:而只要session过期或者失效,则需要执行重新登录等操作.但是!我们对于这 ...
- tomcat 设置session过期时间(四种方式)
1.在tomcat-->conf-->servler.xml文件中定义: <Context path="/test" docBase="/test&qu ...
- Tomcat生成的session持久化到MySQL
Telling Tomcat to save session records in MySQL 此部分内容摘自 MySQL cookbook 3th.具体内容不做翻译,哈哈,懒 The default ...
- tomcat之Session的管理
Session是由服务器端的应用服务器容器(如Tomcat.Jetty)存储的.下面分析一下Tomcat是如何管理Session的. 转自:tomcat架构分析 (Session管理) Tomcat中 ...
- java 中Session 持久化问题
首先: 今天发现了个session 持久化的问题 在Tomcat 停止运行后再启动 session 中保存的东西还会存在 ,百度了一下 原理 1.Session Create 时 2.Sessio ...
- [转]session 持久化问题(重启服务器session 仍然存在)
转:http://xiaolongfeixiang.iteye.com/blog/560800 关于在线人数统计,大都使用SessionListener监听器实现. SessionListener 触 ...
随机推荐
- 坑人的 Javascript 模块化编程 require.js
坑人的 Javascript 模块化编程 require.js
- pytest.4.Fixture
From: http://www.testclass.net/pytest/fixture/ 我们可以简单的把Fixture理解为准备测试数据和初始化测试对象的阶段. 一般我们对测试数据和测试对象的管 ...
- ALGO-118_蓝桥杯_算法训练_连续正整数的和
问题描述 78这个数可以表示为连续正整数的和,++,+++,++. 输入一个正整数 n(<=) 输出 m 行(n有m种表示法),每行是两个正整数a,b,表示a+(a+)+...+b=n. 对于多 ...
- C++进阶--代码复用 继承vs组合
//############################################################################ /* * 代码复用: 继承 vs 组合 * ...
- TCP/IP学习20180627-数据链路层-ethernet
ifconfig :查看主機支持的網絡協議eth0:以太網接口lo:loopback接口 以太网(Ether-net)的定是指数字设备公司( Digital Equipment Corp.).英特尔公 ...
- mysql获取某个表的所有属性名及其数据
MYSQL类实现从数据库相应的表中获取所有属性及其数据,数据为元组类型.返回结果存放在字典中 import pymysql class MYSQL: def __init__(self): pass ...
- Web jsp开发学习——点击菜单页面切换
两个网页使用同一个head,在点击“首页”后,head的“首页”变成绿色,点击“新闻”后,head的“新闻”变成绿色,head的“首页”恢复原来的颜色 head.jsp <%@ page ...
- OpenStack单节点网络设置
一.上传镜像文件 1.上传镜像 2.检索镜像 二.创建网络 1.创建内部网络 2.创建外部网络 3.创建内部网络子网 设置DHCP分配地址池,点击已创建 4.创建外部网络子网 注意:外部网络与仅主机( ...
- mobilehack -转
# mobileHack##工具类网站 [HTML5 与 CSS3 技术应用评估](http://html5please.com/ "html5与css3技术应用评估") [各种奇 ...
- 牛客网剑指Offer——正则表达式匹配
1. 题目描述 请实现一个函数用来匹配包括'.'和'*'的正则表达式.模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次). 在本题中,匹配是指字符串的所有字符匹配整 ...