33Manager组件Tomcat的Session管理机制解析
文章目录
32 | Manager组件:Tomcat的Session管理机制解析
我们可以通过 Request 对象的 getSession 方法来获取 Session,并通过 Session 对象来读取和写入属性值。而 Session 的管理是由 Web 容器来完成的,主要是对 Session 的创建和销毁,除此之外 Web 容器还需要将 Session 状态的变化通知给监听者。
当然 Session 管理还可以交给 Spring 来做,好处是与特定的 Web 容器解耦,Spring Session 的核心原理是通过 Filter 拦截 Servlet 请求,将标准的 ServletRequest 包装一下,换成 Spring 的 Request 对象,这样当我们调用 Request 对象的 getSession 方法时,Spring 在背后为我们创建和管理 Session。
那么 Tomcat 的 Session 管理机制我们还需要了解吗?我觉得还是有必要,因为只有了解这些原理,我们才能更好的理解 Spring Session,以及 Spring Session 为什么设计成这样。今天我们就从 Session 的创建、Session 的清理以及 Session 的事件通知这几个方面来了解 Tomcat 的 Session 管理机制。
Session 的创建
Tomcat 中主要由每个 Context 容器内的一个 Manager 对象来管理 Session。默认实现类为 StandardManager。下面我们通过它的接口来了解一下 StandardManager 的功能:
|
|
不出意外我们在接口中看到了添加和删除 Session 的方法;另外还有 load 和 unload 方法,它们的作用是分别是将 Session 持久化到存储介质和从存储介质加载 Session。
当我们调用 HttpServletRequest.getSession(true) 时,这个参数 true 的意思是“如果当前请求还没有 Session,就创建一个新的”。那 Tomcat 在背后为我们做了些什么呢?
HttpServletRequest 是一个接口,Tomcat 实现了这个接口,具体实现类是: org.apache.catalina.connector.Request 。
但这并不是我们拿到的 Request,Tomcat 为了避免把一些实现细节暴露出来,还有基于安全上的考虑,定义了 Request 的包装类,叫作 RequestFacade,我们可以通过代码来理解一下:
|
|
public class RequestFacade implements HttpServletRequest { protected Request request = null;
public HttpSession getSession(boolean create) { return request.getSession(create); } }
|
|
Context context = getContext(); if (context == null) { return null; }
Manager manager = context.getManager(); if (manager == null) { return null; }
session = manager.createSession(sessionId); session.access();
|
|
@Override public Session createSession(String sessionId) { // 首先判断 Session 数量是不是到了最大值,最大 Session 数可以通过参数设置 if ((maxActiveSessions >= 0) && (getActiveSessions() >= maxActiveSessions)) { rejectedSessions++; throw new TooManyActiveSessionsException( sm.getString(“managerBase.createSession.ise”), maxActiveSessions); }
// 重用或者创建一个新的 Session 对象,请注意在 Tomcat 中就是 StandardSession // 它是 HttpSession 的具体实现类,而 HttpSession 是 Servlet 规范中定义的接口 Session session = createEmptySession();
// 初始化新 Session 的值 session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60); String id = sessionId; if (id == null) { id = generateSessionId(); } session.setId(id);// 这里会将 Session 添加到 ConcurrentHashMap 中 sessionCounter++;
// 将创建时间添加到 LinkedList 中,并且把最先添加的时间移除 // 主要还是方便清理过期 Session SessionTiming timing = new SessionTiming(session.getCreationTime(), 0); synchronized (sessionCreationTiming) { sessionCreationTiming.add(timing); sessionCreationTiming.poll(); } return session }
|
|
protected Map<String, Session> sessions = new ConcurrentHashMap<>();
|
|
public class StandardSession implements HttpSession, Session, Serializable {
protected ConcurrentMap<String, Object> attributes = new ConcurrentHashMap<>();
protected long creationTime = 0L;
protected transient volatile boolean expiring = false;
protected transient StandardSessionFacade facade = null;
protected String id = null;
protected volatile long lastAccessedTime = creationTime;
protected transient ArrayList
|
|
public void backgroundProcess() { // processExpiresFrequency 默认值为 6,而 backgroundProcess 默认每隔 10s 调用一次,也就是说除了任务执行的耗时,每隔 60s 执行一次 count = (count + 1) % processExpiresFrequency; if (count == 0) // 默认每隔 60s 执行一次 Session 清理 processExpires(); }
/** * 单线程处理,不存在线程安全问题 */ public void processExpires() {
// 获取所有的 Session Session sessions[] = findSessions(); int expireHere = 0 ; for (int i = 0; i < sessions.length; i++) { // Session 的过期是在 isValid() 方法里处理的 if (sessions[i]!=null && !sessions[i].isValid()) { expireHere++; } } }
|
|
public interface HttpSessionListener extends EventListener { //Session 创建时调用 public default void sessionCreated(HttpSessionEvent se) { }
//Session 销毁时调用 public default void sessionDestroyed(HttpSessionEvent se) { } }
|
|
session.setId(id);
@Override public void setId(String id, boolean notify) { // 如果这个 id 已经存在,先从 Manager 中删除 if ((this.id != null) && (manager != null)) manager.remove(this);
this.id = id;
// 添加新的 Session if (manager != null) manager.add(this);
// 这里面完成了 HttpSessionListener 事件通知 if (notify) { tellNew(); } }
|
|
public void tellNew() {
// 通知 org.apache.catalina.SessionListener fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
// 获取 Context 内部的 LifecycleListener 并判断是否为 HttpSessionListener Context context = manager.getContext(); Object listeners[] = context.getApplicationLifecycleListeners(); if (listeners != null && listeners.length > 0) {
// 创建 HttpSessionEvent HttpSessionEvent event = new HttpSessionEvent(getSession()); for (int i = 0; i < listeners.length; i++) { // 判断是否是 HttpSessionListener if (!(listeners[i] instanceof HttpSessionListener)) continue;
HttpSessionListener listener = (HttpSessionListener) listeners[i]; // 注意这是容器内部事件 context.fireContainerEvent(“beforeSessionCreated”, listener); // 触发 Session Created 事件 listener.sessionCreated(event);
// 注意这也是容器内部事件 context.fireContainerEvent(“afterSessionCreated”, listener);
} } }
|
|
文章作者
上次更新 10100-01-10