Tomcat 作為最廣泛使用的 Servlet 容器,其 Session 管理機制是 Java Web 開發者必須理解的核心基礎設施。本文基於 Tomcat 7/8/9 源碼架構,深度剖析 StandardManager、PersistentManager、DeltaManager、BackupManager 等管理器的啟動流程、Session 創建/存取/銷毀、持久化、集群同步完整鏈路。
Manager (介面)
│
├─ ManagerBase (抽象基類)
│ │
│ ├─ StandardManager (標準實現,預設)
│ │ └─ 持久化到 SESSIONS.ser
│ │
│ ├─ PersistentManager (可插拔 Store)
│ │ ├─ FileStore (檔案系統)
│ │ └─ JDBCStore (資料庫)
│ │
│ └─ ClusterManagerBase (集群基類)
│ │
│ ├─ DeltaManager (全量同步,預設集群)
│ └─ BackupManager (主備同步)
│
Session (介面)
│
├─ StandardSession (標準實現)
├─ DeltaSession (增量屬性追蹤)
└─ ClusterSession (集群標識)
// StandardContext.startInternal() 關鍵片段
protected synchronized void startInternal() throws LifecycleException {
// ... 省略 ...
// 1. 判斷是否需要集群 Manager
Manager contextManager = null;
if (manager == null) {
if (getCluster() != null && distributable) {
// 集群環境:由 Cluster 創建 Manager (DeltaManager/BackupManager)
contextManager = getCluster().createManager(getName());
} else {
// 單機環境:預設 StandardManager
contextManager = new StandardManager();
}
}
// 2. 設定 Manager 到 Context
if (contextManager != null) {
setManager(contextManager);
}
// 3. 註冊到集群 (若適用)
if (manager != null && getCluster() != null && distributable) {
getCluster().registerManager(manager);
}
// 4. 啟動 Manager (觸發 Lifecycle 狀態機)
if (manager instanceof Lifecycle) {
((Lifecycle) manager).start();
}
// ... 省略 ...
}
// StandardManager.startInternal()
protected synchronized void startInternal() throws LifecycleException {
// 1. 強制初始化隨機數生成器 (用於 Session ID)
generateSessionId();
// 2. 載入持久化 Session (若存在)
try {
load(); // 讀取 work/Catalina/localhost//SESSIONS.ser
} catch (Throwable t) {
log.error("載入持久化 Session 失敗", t);
}
setState(LifecycleState.STARTING);
}
// ManagerBase.generateSessionId()
protected synchronized String generateSessionId() {
byte random[] = new byte[16];
String jvmRoute = getJvmRoute(); // 集群路由標識
StringBuilder buffer = new StringBuilder();
do {
// 雙重雜湊:MD5(random) → 十六進制字串
getRandomBytes(random); // 從 /dev/urandom 或 SecureRandom 獲取
random = getDigest().digest(random); // MD5 雜湊
// 編碼為十六進制
for (byte b : random) {
buffer.append(toHex((b >> 4) & 0xF));
buffer.append(toHex(b & 0xF));
}
} while (sessions.containsKey(buffer.toString())); // 確保唯一
if (jvmRoute != null) buffer.append('.').append(jvmRoute);
return buffer.toString();
}
work/Catalina///SESSIONS.ser StandardSession 實現 Serializable,包含 id、creationTime、lastAccessedTime、maxInactiveInterval、attributes (屬性 Map)Serializable,否則序列化失敗// context.xml 配置範例
maxIdleSwap 秒 → 交換到 Store (釋放記憶體)// ApplicationFilterChain.internalDoFilter() → StandardSessionFacade
// 1. 從 Request 獲取 Session
HttpSession session = request.getSession(); // 或 getSession(false)
// 2. 底層調用 Manager.findSession(sessionId)
// StandardManager.findSession()
public Session findSession(String id) {
return sessions.get(id); // ConcurrentHashMap O(1) 查找
}
// 3. 存取屬性觸發 access()/endAccess() 更新 lastAccessedTime
session.setAttribute("key", value); // → StandardSession.setAttribute()
session.getAttribute("key"); // → StandardSession.getAttribute()
// ManagerBase.backgroundProcess() - 由 ContainerBackgroundProcessor 定期調用
public void backgroundProcess() {
// 1. 清理過期 Session
Session[] expired = findExpiredSessions(); // 遍歷 sessions 檢查 isValid() && isExpired()
for (Session session : expired) {
try { session.expire(); } catch (Throwable t) { log.warn(...); }
}
// 2. PersistentManager 額外邏輯
if (this instanceof PersistentManager) {
((PersistentManager) this).processPersistenceChecks(); // Swap/Backup
}
}
// 配置清理頻率 (context.xml)
session.setAttribute() → DeltaSession.serialize() → Cluster.send()DeltaSession 只序列化變更的屬性,而非整個 Session| 場景 | 建議配置 | 原因 |
|---|---|---|
| 單機高併發 | StandardManager + 增加 maxActiveSessions | 避免 Session 洪峰 OOM |
| 需重啟保持登入 | StandardManager (預設持久化) 或 PersistentManager + FileStore | SESSIONS.ser 自動載入 |
| 小規模集群 (2-5節點) | DeltaManager + 專用網卡 + 組播/單播 | 實現簡單、延遲低 |
| 大規模集群 (>10節點) | BackupManager 或 外部化 Session (Spring Session + Redis) | 避免全量同步記憶體/網絡爆炸 |
| 微服務/雲原生 | Spring Session + Redis + 樹莓派模式 | 無狀態、水平擴展、多語言支援 |
distributable 標記、jvmRoute 配置、防火牆組播端口Serializable,日誌會報 NotSerializableExceptionmaxActiveSessions 未限制、Session 屬性存放大對象、過期清理間隔過長ReplicationValve 過濾配置不當spring.session.store-type=redis spring.redis.host=localhost spring.redis.port=6379 spring.session.redis.namespace=spring:session spring.session.timeout=30m
Tomcat Session 管理從 StandardManager 的單機持久化,到 DeltaManager 的集群廣播,再到現代 Spring Session 的外部化存儲,體現了狀態管理外部化的架構演進趨勢。理解底層機制,能在遷移、排錯、調優時做出正確決策。