PersistentManagerBase
Session
管理器中打标持久化Session
的父类, 虽然StandardManager
也可以将Session
持久化,但是只是将Session
持久化为一个 文件
, PersistentManagerBase
类和StandardManager
类的区别在于前者的存储器的表现形式可以有多种,比如数据库,文件
等。
PersistentManager
在PersistentManagerBase
基础上增加了两个属性。
PersistentManager
代表的是持久化session的管理器
, PersistentManager
类定义中有个变量org.apache.catalina.Store
, 该变量表示session管理器持久化session的方式
Store
持久化存储方式的抽象类,定义了一些基本方法,例如save(),load(),keys(),clear()
等。save()
用来将session持久化到持久性介质
中。load()
方法从持久化介质中读取到内存
中,keys()
则返回所有的sessionId数组
。clear()
则清除所有的session。
StoreBase
抽象类,对Store
作了基本实现。
FileStore
该类会将session
对象存储到某个文件中,文件名会使用session对象的标识符再加上一个后缀.session
构成。文件位于临时目录下,也可以调用FileStore
类的setDirectroy()
方法修改目录。
JDBCStore
该类将session对象通过jdbc存入数据库,因此使用该类需要使用jdbc
链接。
Request
的doGetSession()
方法中,我们之前默认manager
实现类是StandardManager
,如果tomcat中配置的是PersistentManager
,那么manager.findSession(requestedSessionId)
会略有不同
manager.findSession(requestedSessionId)
@Override
public Session findSession(String id) throws IOException {
//调用父类的findSession() 也就是ManagerBase类中的findSession,从现有内存中查找是否有指定的session
Session session = super.findSession(id);
// 代码运行到这里,因为我们不确定是否有别的线程要移除这个session,所以最保险的办法就是加锁再次尝试获取该session
// 如果有其他代码正在执行 swapOut(将内存session持久化到介质中),那么我们应该返回null,如果没有的话,那么我们就可以安全的访问这个session
if(session != null) {
synchronized(session){
session = super.findSession(session.getIdInternal());
if(session != null){
// To keep any external calling code from messing up the
// concurrency.
session.access();
session.endAccess();
}
}
}
// 再次判断
if (session != null)
return session;
// See if the Session is in the Store
//从持久化介质中查找 session是否存在
session = swapIn(id);
return session;
}
swapIn()
从持久化介质中查找 session是否存在
protected Session swapIn(String id) throws IOException {
if (store == null)
return null;
Object swapInLock = null;
/*
* The purpose of this sync and these locks is to make sure that a
* session is only loaded once. It doesn't matter if the lock is removed
* and then another thread enters this method and tries to load the same
* session. That thread will re-create a swapIn lock for that session,
* quickly find that the session is already in sessions, use it and
* carry on.
*/
synchronized (this) {
swapInLock = sessionSwapInLocks.get(id);
if (swapInLock == null) {
swapInLock = new Object();
sessionSwapInLocks.put(id, swapInLock);
}
}
Session session = null;
synchronized (swapInLock) {
// First check to see if another thread has loaded the session into
// the manager
session = sessions.get(id);
if (session == null) {
try {
if (SecurityUtil.isPackageProtectionEnabled()){
try {
session = AccessController.doPrivileged(
new PrivilegedStoreLoad(id));
} catch (PrivilegedActionException ex) {
Exception e = ex.getException();
log.error(sm.getString(
"persistentManager.swapInException", id),
e);
if (e instanceof IOException){
throw (IOException)e;
} else if (e instanceof ClassNotFoundException) {
throw (ClassNotFoundException)e;
}
}
} else {
//加载session
//1111111
session = store.load(id);
}
} catch (ClassNotFoundException e) {
String msg = sm.getString(
"persistentManager.deserializeError", id);
log.error(msg, e);
throw new IllegalStateException(msg, e);
}
if (session != null && !session.isValid()) {
log.error(sm.getString(
"persistentManager.swapInInvalid", id));
session.expire();
removeSession(id);
session = null;
}
if (session != null) {
if(log.isDebugEnabled())
log.debug(sm.getString("persistentManager.swapIn", id));
session.setManager(this);
// make sure the listeners know about it.
((StandardSession)session).tellNew();
add(session);
((StandardSession)session).activate();
// endAccess() to ensure timeouts happen correctly.
// access() to keep access count correct or it will end up
// negative
session.access();
session.endAccess();
}
}
}
// Make sure the lock is removed
synchronized (this) {
sessionSwapInLocks.remove(id);
}
return session;
}
Store.load(id)
Store.save()
ManagerBase.backgroundProcess()
@Override
public void backgroundProcess() {
count = (count + 1) % processExpiresFrequency;
if (count == 0)
processExpires();
}
模板方法模式, 子类拓展 processExpires
PersistentManagerBase.processExpires()
public void processPersistenceChecks() {
processMaxIdleSwaps();
processMaxActiveSwaps();
processMaxIdleBackups();
}
processMaxIdleSwaps()
调用swapOut()
writeSession()
调用的是Store.save()
processMaxActiveSwaps(),processMaxIdleBackups()
方法类似
PersistentManager
与StandardManager
区别在于,PersistentManager
在StandardManager
的基础上额外增加了存储
的功能,不管查找,删除,还是保存都需要在内存和存储器中同时进行。
在web.xml中配置 session 的过期时间,默认30min
<session-config>
<session-timeout>30session-timeout>
session-config>
在server.xml中配置 session管理器,默认StandardManager
可以不配置,如果需要配置全局的session manager,
可以在conf/context.xm
l中配置
1. StandardManager
当Tomcat服务器关闭或重启,或者Web应用被重新加载时,会对在内存中的HttpSession
对象进行持久化, 并把它们保存到文件系统中,默认的文件为$CATALINA_HOME/work/Catalina/hostname/applicationname/SESSIONS.server
2. PersistenManager
<Manager className="org.apache.catalina.session.PersistentManager"
saveOnRestart="true"
maxActiveSessions="-1"
minIdleSwap="60"
maxIdleSwap="60"
maxIdleBackup="60"
>
<Store
className="org.apache.catalina.session.JDBCStore"
driverName="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://url?user=user&password=psd"
sessionTable="tomcat_session"
sessionIdCol="session_id"
sessionDataCol="session_data"
sessionValidCol="session_valid"
sessionMaxInactiveCol="max_inactive"
sessionLastAccessedCol="last_access"
sessionAppCol="app_name"
/>
Manager>
saveOnRestart
:是否在重启的时候加载保存sessionmaxActiveSessions
:最大允许session数量,-1 不限制minIdleSwap
:最小空闲时间,超出将会被转存到存储器中maxIdleSwap
:最大空闲时间,超出将会被转存到存储器中Store相关:
directory
:采用FileStore的时候指存储session的目录sessionTable
:存储session的表名sessionIdCol
:sessionid列名sessionDataCol
:sessionData列名sessionValidCol
:session是否有效列名sessionMaxInactiveCol
:session最大闲置时间列名sessionLastAccessedCol
:session上次访问时间列名sessionAppCol
:session归属的应用名称列名