前言
在上文Nginx+Tomcat关于Session的管理中简单介绍了如何使用redis来集中管理session,本文首先将介绍默认的管理器是如何管理Session的生命周期的,然后在此基础上对Redis集中式管理Session进行分析。
Tomcat Manager介绍
上文中在Tomcat的context.xml中配置了Session管理器RedisSessionManager,实现了通过redis来存储session的功能;Tomcat本身提供了多种Session管理器,如下类图:
1.Manager接口类
定义了用来管理session的基本接口,包括:createSession,findSession,add,remove等对session操作的方法;还有getMaxActive,setMaxActive,getActiveSessions活跃会话的管理;还有Session有效期的接口;以及与Container相关联的接口;
2.ManagerBase抽象类
实现了Manager接口,提供了基本的功能,使用ConcurrentHashMap存放session,提供了对session的create,find,add,remove功能,并且在createSession中了使用类SessionIdGenerator来生成会话id,作为session的唯一标识;
3.ClusterManager接口类
实现了Manager接口,集群session的管理器,Tomcat内置的集群服务器之间的session复制功能;
4.ClusterManagerBase抽象类
继承了ManagerBase抽象类,实现ClusterManager接口类,实现session复制基本功能;
5.PersistentManagerBase抽象类
继承了ManagerBase抽象类,实现了session管理器持久化的基本功能;内部有一个Store存储类,具体实现有:FileStore和JDBCStore;
6.StandardManager类
继承ManagerBase抽象类,Tomcat默认的Session管理器(单机版);对session提供了持久化功能,tomcat关闭的时候会将session保存到javax.servlet.context.tempdir路径下的SESSIONS.ser文件中,启动的时候会从此文件中加载session;
7.PersistentManager类
继承PersistentManagerBase抽象类,如果session空闲时间过长,将空闲session转换为存储,所以在findsession时会首先从内存中获取session,获取不到会多一步到store中获取,这也是PersistentManager类和StandardManager类的区别;
8.DeltaManager类
继承ClusterManagerBase,每一个节点session发生变更(增删改),都会通知其他所有节点,其他所有节点进行更新操作,任何一个session在每个节点都有备份;
9.BackupManager类
继承ClusterManagerBase,会话数据只有一个备份节点,这个备份节点的位置集群中所有节点都可见;相比较DeltaManager数据传输量较小,当集群规模比较大时DeltaManager的数据传输量会非常大;
10.RedisSessionManager类
继承ManagerBase抽象类,非Tomcat内置的管理器,使用redis集中存储session,省去了节点之间的session复制,依赖redis的可靠性,比起sessin复制扩展性更好;
Session的生命周期
1.解析获取requestedSessionId
当我们在类中通过request.getSession()时,tomcat是如何处理的,可以查看Request中的doGetSession方法:
protected Session doGetSession(boolean create) {
// There cannot be a session if no context has been assigned yet
Context context = getContext();
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 = 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 ((response != null) &&
context.getServletContext().getEffectiveSessionTrackingModes().
contains(SessionTrackingMode.COOKIE) &&
response.getResponse().isCommitted()) {
throw new IllegalStateException
(sm.getString("coyoteRequest.sessionCreateCommitted"));
}
// Re-use session IDs provided by the client in very limited
// circumstances.
String sessionId = getRequestedSessionId();
if (requestedSessionSSL) {
// If the session ID has been obtained from the SSL handshake then
// use it.
} else if (("/".equals(context.getSessionCookiePath())
&CBCBOB]B."y+yi)"My.+yy."y/f9l9kb,Y\#9b!b*&{Y\\[\\J
{[OH
\\[[\YH\[[\\Y
J{Z\\[[\Y9.\^\˙\]X[Y[[]X]\\
[]X]\\HX[^\]X]\\Y\\[JJyam.+y. 9.*..Yyy."f9l9kb,ZY&Bzay. 9."{c!)yl,y+k/f9. 9.*!#9"yay/f9QUSWSSTWTTUQTB[BOQUS;n9/f9/z-eam.y/f[OBOWS(\[]X]J
x[[[P]X]J
z)&/f;OBOSTWTTUQT 9.*\]Y\+`d#`.b-/f;+b%OBBY\\[\\J
yb\[az`+"z!#kB]\HHBH\H\]NBXXX[\\J
H]\X[[YS\˙\KX[[YJ
H]\[]X]\\\J
JNCBOB]B 9.*\]Y\+`d#b)"z!#k"z!#kcy/f;k)"TWSj#9/a+y"PSTWTTUQTiy;Bh."yz`+iyb*XY[9c&B/eyiBB.". :".+y.9ayk'b\[+'[Y\.+y\^\\iyi!9[/;/a+Y\\[X[Yay.i9yB]\HHBH\H\]NBXX\^\\HCBOB]By.#y`fi!9.am+*9.Y\kf9#9am*R[\[9y+{B]\HHBH\H\]NBY\˙^\J[\RYX^[X]R[\[
JNOB]B[[OH.[BB+9!9.[fj;.\Y\[[X[Y+ez/c[f+yo#!/a+mj9/z-e9kyfj;i99aj9.n9#ybyfj9yb;BBHYHXK[X[[[H^\[\H[[[+*.#ze&y`"y B."l,y+j:`yk{n#9&)9ki.h9"y`9n+b{.g&i&i&+/c. |