Tomcat 是如何管理 Session 的?
Tomcat 管理 Session 的机制是一个经典且高效的过程,主要涉及会话的创建、ID生成与传递、存储结构、生命周期管理以及持久化。
核心组件是 Tomcat 容器中的 Manager 接口及其实现类(主要是 StandardManager)。
以下是 Tomcat 管理 Session 的详细内部机制:
1. 核心架构组件
在 Tomcat 的架构中,Session 管理属于 Context 容器(即 Web 应用)的一部分。
- Manager (管理器): 负责管理所有的 Session 对象。它负责创建、查找、更新、销毁 Session。
- 默认实现是
org.apache.catalina.session.StandardManager。
- 默认实现是
- Session (会话对象): 对应
javax.servlet.http.HttpSession接口,Tomcat 内部的实现是org.apache.catalina.session.StandardSession。
2. Session 的创建与 ID 生成
当用户代码调用 request.getSession() 或 request.getSession(true) 时,流程如下:
- 检查是否存在: Tomcat 首先检查 HTTP 请求中是否包含 Session ID(通常在 Cookie 或 URL 中)。
- 查找: 如果有 ID,
Manager会尝试在内存中查找对应的 Session 对象。如果找到且未过期,直接返回。 - 创建: 如果没有 ID,或者 ID 对应的 Session 已失效,
Manager会创建一个新的StandardSession对象。 - 生成 Session ID:
- Tomcat 使用
SessionIdGenerator生成一个唯一的字符串(通常是 32 位十六进制字符)。 - 为了安全,它使用
SecureRandom类来保证 ID 的随机性,防止被预测(Session 劫持)。 - 生成的 ID 就是我们常见的
JSESSIONID。
- Tomcat 使用
3. Session ID 的传递机制 (Tracking)
服务器创建 Session 后,必须告诉客户端这个 ID,以便后续请求能识别身份。Tomcat 支持两种方式:
- Cookie (默认 & 首选):
- Tomcat 会在 HTTP 响应头中添加
Set-Cookie: JSESSIONID=xxxxxx; Path=/; HttpOnly。 - 浏览器收到后保存,后续请求会自动带上
Cookie: JSESSIONID=xxxxxx。
- Tomcat 会在 HTTP 响应头中添加
- URL Rewriting (URL 重写):
- 如果客户端禁用了 Cookie,Tomcat 支持将 ID 拼接到 URL 后面,格式如:
http://example.com/index.jsp;jsessionid=xxxxxx。 - 这需要开发者在代码中使用
response.encodeURL()来处理链接。
- 如果客户端禁用了 Cookie,Tomcat 支持将 ID 拼接到 URL 后面,格式如:
4. 内存存储结构
StandardManager 将所有的 Session 对象存储在 JVM 的堆内存中。
- 数据结构: 内部维护了一个
ConcurrentHashMap<String, Session>。- Key: Session ID。
- Value: Session 对象实例。
- 并发控制: 由于是
ConcurrentHashMap,它支持高并发的读写操作。
5. 生命周期与过期处理
Session 不能永久存在,否则会撑爆内存。Tomcat 通过后台线程进行清理:
- 后台线程: Tomcat 有一个
ContainerBackgroundProcessor线程,定期(默认每 10 秒)调用Manager.backgroundProcess()。 - 检查逻辑:
- 遍历 Map 中的 Session 对象。
- 检查
isValid()。 - 计算:
当前时间 - 最后访问时间 (LastAccessedTime)。 - 如果差值大于
MaxInactiveInterval(默认 30 分钟,可在web.xml配置),则判定为过期。
- 销毁:
- 调用
session.expire()。 - 触发
HttpSessionListener.sessionDestroyed()监听器。 - 从 Map 中移除该对象,释放内存。
- 调用
6. 持久化 (Persistence) - 钝化与活化
为了防止 Tomcat 重启导致用户掉线,或者为了节省内存,Tomcat 提供了 Session 的持久化机制。
A. StandardManager (默认行为 - 重启恢复)
当 Tomcat 正常关闭(Shutdown)或重启时:
- 序列化 (钝化):
StandardManager会将内存中所有有效的 Session 对象序列化到一个文件SESSIONS.ser中(通常位于work/Catalina/localhost/你的应用名/目录下)。 - 反序列化 (活化): 当 Tomcat 再次启动时,会读取该文件,将 Session 恢复到内存中。
- 注意: 放入 Session 中的对象必须实现
java.io.Serializable接口,否则无法持久化。
- 注意: 放入 Session 中的对象必须实现
B. PersistentManager (高级配置 - 内存置换)
如果配置了 PersistentManager,Tomcat 可以将长期不活跃但未过期的 Session 移出内存。
- Store: 可以配置保存到文件系统 (
FileStore) 或数据库 (JDBCStore)。 - 策略: 当内存中 Session 数量超过阈值,或某个 Session 闲置超过一定时间(
maxIdleSwap),将其“钝化”到硬盘/数据库,腾出内存。当该 Session 再次被访问时,再将其“活化”回内存。
7. 集群环境下的 Session 管理 (分布式)
在多台 Tomcat 组成的集群中,默认的 StandardManager 无法共享 Session。Tomcat 提供了以下方案:
- Session 复制 (DeltaManager):
- 利用 TCP 组播,将一台 Tomcat 上的 Session 变更复制到集群中所有其他节点。
- 缺点:网络流量大,不适合大规模集群。
- BackupManager:
- Session 只复制到一个备份节点,而不是所有节点。
- 第三方存储 (主流方案):
- 实际上,现代架构很少使用 Tomcat 自带的集群 Session 管理。
- 通常使用 Spring Session + Redis。Tomcat 此时只负责处理请求,Session 数据实际存储在外部的 Redis 中,实现了无状态化。
总结
Tomcat 管理 Session 的流程可以概括为:
- 生成安全的 ID。
- 通过 Cookie 交换 ID。
- 使用 ConcurrentHashMap 在内存中存储。
- 通过后台线程定期清理过期会话。
- 通过序列化文件处理重启时的数据保留。
右滑查看面试常问