前言
在上文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()) && isRequestedSessionIdFromCookie())) { /* This is the common(ish) use case: using the same session ID with * multiple web applications on the same host. Typically this is * used by Portlet implementations. It only works if sessions are * tracked via cookies. The cookie must have a path of "/" else it * won't be provided for requests to all web applications. * * Any session ID provided by the client should be for a session * that already exists somewhere on the host. Check if the context * is configured for this to be confirmed. */ if (context.getValidateClientProvidedNewSessionId()) { boolean found = false; for (Container container : getHost().findChildren()) { Manager m = ((Context) container).getManager(); if (m != null) { try { if (m.findSession(sessionId) != null) { found = true; break; } } catch (IOException e) { // Ignore. Problems with this manager will be // handled elsewhere. } } } if (!found) { sessionId = null; } } } else { sessionId = null; } session = manager.createSession(sessionId); // Creating a new session cookie based on that session if ((session != null) && (getContext() != null) && getContext().getServletContext(). getEffectiveSessionTrackingModes().contains( SessionTrackingMode.COOKIE)) { Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie( context, session.getIdInternal(), isSecure()); response.addSessionCookieInternal(cookie); } if (session == null) { return null; } session.access(); return session;}
评论列表()