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设计模式与开发》笔记 1.面向对象的JavaScript
多态 封装 原型模式 基于原型模式的继承javascript对象 1.多态 多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果.换句话说,给不同的对象发哦少年宫同一个消 ...
- jenkins 邮件配置 二 ***
Jenkins 有两种邮件通知方式: 1.Jenkins自带的“E-mail Notification” 2.插件:Extended E-mail Notification,是可编辑的邮件配置方式. ...
- jenkins 邮件配置一
jenkins默认的邮件通知 我先讲解下,默认的. jenkins默认就有一个邮件通知,只是太简单的,不能个性化或者说定制化. 设置系统管理员邮件地址 邮件通知 ①SMTP服务器:如果你使用的是公司邮 ...
- vlan交换机的端口模式有哪几种
一 端口类型1 ,Access用户模式2 ,Trunk链路模式3 ,Hybrid模式(跟Trunk很类似但比trunk高级)二 端口介绍2.1 ,Access类型端口:只允许默认vlan的以太网帧,也 ...
- [蓝桥杯]ALGO-49.算法训练_寻找数组中最大值
题目描述: 问题描述 对于给定整数数组a[],寻找其中最大值,并返回下标. 输入格式 整数数组a[],数组元素个数小于1等于100.输出数据分作两行:第一行只有一个数,表示数组元素个数:第二行为数组的 ...
- ALGO-125_蓝桥杯_算法训练_王、后传说(DFS)
问题描述 地球人都知道,在国际象棋中,后如同太阳,光芒四射,威风八面,它能控制横.坚.斜线位置. 看过清宫戏的中国人都知道,后宫乃步步惊心的险恶之地.各皇后都有自己的势力范围,但也总能找到相安无事的办 ...
- 在windows 7中vagrant up 无反应,没任何信息输出
本文转载自:https://blog.csdn.net/cow66/article/details/77993908 我的系统是windows 7 安装了vagrant,当运行vagrant up时, ...
- 窗口事件onresize
在做自适应布局的时候,我们常常需要根据窗口不同的分辨率给出不同布局和样式,今天说的onresize便能帮我们实现这一效果. onresize事件在窗口或者框架的大小发生改变的时候会被调用,下面我们用一 ...
- [转]Android 代码自动提示功能
源地址http://blog.sina.com.cn/s/blog_7dbac12501019mbh.html 或者http://blog.csdn.net/longvslove/article/de ...
- 构建Redis主从镜像
构建Redis的基础镜像,然后基于这个基础镜像构建主Redis镜像和从Redis镜像. 1.构建Redis基础镜像 创建redis基础镜像目录 [root@localhost mnt]# mkdir ...