关于Tomcat的Session,我们都知道默认是保存在内存中。为了解决重启应用服务器Session丢失的问题,Tomcat内部的StandardManager会在正常的关闭时钝化活动的Session 并在重启的时候重新加载。
而Tomcat在 StandardManager 之外,还提供了对应持久化Session的Manager实现: PersistentManager ,目前对应的有两种持久化的实现
FileStore
JDBCStore
分别是将活动的Session保存在磁盘上和保存到数据库中。
本次我们以FileStore为例,来分析下PersistentManager在Tomcat中的实现。
PersistentManager的配置基本上是这样的:
<Manager className="org.apache.catalina.session. PersistentManager " debug="0" saveOnRestart="true" maxActiveSessions="-1" minIdleSwap="-1" maxIdleSwap="5" maxIdleBackup="3" >
< Store className="org.apache.catalina.session.FileStore" directory="/home/mytestsession"/>
</Manager>
对于FileStore和JDBCStore,基本配置都类似,有差异的只是 Store 中对应的具体属性,比如JDBCStore需要额外指定数据的用户名和密码等。上面的配置可以直接 用于FileStore。
其中,像 maxIdleBackup
、 maxIdleSwap
、 minIdleSwap
默认都是关闭的,默认值都是-1。当然,我们上面的配置是修改过的。默认的行为会和StandardManager一致,即在关闭重启时进行Session的钝化和解析。
而当按照我们上面的配置启动Tomcat后,服务器会根据maxIdleBackup的时间,以秒为单位,进行空闲Session的持久化。在配置的目录中,会生成以sessionId为文件名.session的文件
例如:5E62468BFF33CF7DE28464A76416B85E.session
主要参数说明:
saveOnRestart -当服务器关闭时,是否要将所有的session持久化;
maxActiveSessions - 可处于活动状态的session数;
minIdleSwap/maxIdleSwap -session处于不活动状态最短/长时间(s),sesson对象转移到File Store中;
maxIdleBackup -大于这一时间时,会将session备份。
写文件:
public void save(Session session) throws IOException {
// Open an output stream to the specified pathname, if any
File file = file(session.getIdInternal());
if (file == null) {
return;
}
if (manager.getContext().getLogger().isDebugEnabled()) {
manager.getContext().getLogger().debug(sm.getString(getStoreName() + ".saving",
session.getIdInternal(), file.getAbsolutePath()));
}
try (FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(fos))) {
((StandardSession)session). writeObjectData (oos);
}
}
我们来看load操作,和StandardManager加载Session基本一致,先创建空的session,再readObjectData:
public Session load(String id) {
File file = file(id);
Context context = getManager().getContext();
try (FileInputStream fis = new FileInputStream(file.getAbsolutePath());
ObjectInputStream ois = getObjectInputStream(fis)) {
StandardSession session = (StandardSession) manager.createEmptySession();
session.readObjectData(ois);
session.setManager(manager);
return session;
} catch (FileNotFoundException e) {
return null;
}
}
删除操作:
public void remove(String id) throws IOException {
File file = file(id);
file.delete();
}
而这些load,remove等操作的触发点,就是我们之前提到过的 后台线程 :我们在前面分析过期的session是如何处理的时候,曾提到过,可以移步这里: 对于过期的session,Tomcat做了什么?
都是使用 backgroundProcess
public void backgroundProcess() {
count = (count + 1) % processExpiresFrequency;
if (count == 0)
processExpires();
}
在 PersistentManager
的processExpires方法中,重点有以下几行
processPersistenceChecks();
if (getStore() instanceof StoreBase) {
((StoreBase) getStore()).processExpires();
}
其中在 processPersistenceChecks
中,就会对我们上面配置的几项进行检查,判断是否要进行session文件的持久化等操作
/**
* Called by the background thread after active sessions have been checked
* for expiration, to allow sessions to be swapped out, backed up, etc.
* /
public void processPersistenceChecks() {
processMaxIdleSwaps();
processMaxActiveSwaps();
processMaxIdleBackups();
}
此外,通过配置 pathname
为空,即可禁用session的持久化策略,在代码中,判断pathname为空时,不再创建持久化文件,从而禁用此功能。
<Manager pathname="" />
总结下,正如文档中所描述,StandardManager所支持的重启时加载已持久化的Session这一特性,相比PersistentManager只能算简单实现。要实现更健壮、更符合生产环境的重启持久化,最好使用PersistentManager并进行恰当的配置。
关于Session,你可能还想了解这些:
详解集群内Session高可用的实现原理
深入Tomcat源码分析Session到底是个啥!
禁用Cookie后,Session怎么样使用?
扫描或长按下方二维码,共同成长!