今天使用一種很臨時的方案解決 Session
泄漏的問題:縮短 Session
的過期時間。這種方法雖然簡單,但卻非常有效。然而,這引發了一個問題:我們應該將過期時間設置為多短呢?在 Spring Boot 中,最短的過期時間是 60 秒。如果你配置的值小于 60 秒,系統會將其默認設置為 60 秒。這是由 org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getSessionTimeoutInMinutes
方法決定的:
private long getSessionTimeoutInMinutes() {Duration sessionTimeout = getSession().getTimeout();if (isZeroOrLess(sessionTimeout)) {return 0;}return Math.max(sessionTimeout.toMinutes(), 1);}
這時候組內有同學說,過短的過期時間是否會影響服務的性能,例如,是否需要更頻繁地刪除 Session
?
為了解答這個問題,我們需要理解 “Tomcat 是如何清除 Session
的”,以及 “Tomcat 中 Session
的更新機制是否會受到 Session
過期時間設置的長短的影響”。
其實根據經驗,結合 Redis 刪除過期 Key,以及 ThreadLocal
清除過期 entry
等存在類似場景的框架,過期清除機制無非就是兩種策略,以及一些細微的處理差異:
- 惰性刪除:只在數據被訪問時才檢查和刪除過期的數據
- 每次只檢查當前數據
- 順帶檢查周邊數據
- 定期刪除:后臺定時進行掃描
- 全量掃描
- 隨機掃描
我相信 Tomcat 的處理方式也不會脫離這兩種策略。帶著這個思考,那么接下來就源碼分析一下 Tomcat 到底是如何清除過期 Session
的。
關于 Session
,Tomcat 提供了一個常用的函數 org.apache.catalina.session.StandardSession#isValid
,用于判斷 Session
是否有效。如果 Session
已經無效,該函數會調用 org.apache.catalina.session.StandardSession#expire(boolean)
使 Session
過期(移除)。這表明 Tomcat 對 Session
的處理采用了惰性刪除機制。
Tomcat 提供了幾種不同的 Session
持久化策略,主要通過實現不同的 Manager
和 Store
來實現:
- 內存持久化:這是 Tomcat 的默認策略,由
StandardManager
實現。所有的 Session 都保存在內存中。當 Tomcat 重啟或者出現故障時,所有的 Session 信息都會丟失。 - 文件持久化:由
PersistentManager
和FileStore
實現。不活躍的 Session 會被保存到文件系統中,從而釋放內存。當 Session 再次變得活躍時,可以從文件系統中恢復。 - 數據庫持久化:由
PersistentManager
和JDBCStore
實現。不活躍的 Session 會被保存到數據庫中。這種策略適合于需要在多個 Tomcat 實例之間共享 Session 的情況。 - 分布式 Session:在一個 Tomcat 集群中,可以通過實現
ClusterableSession
和DeltaManager
或BackupManager
來實現 Session 的復制或者備份,從而實現 Session 的分布式管理。
本文關注的是內存持久化策略,即 StandardManager
。它有一個函數 org.apache.catalina.session.ManagerBase#processExpires
,該函數遍歷所有的 Session
,對每個 Session
判斷是否過期。如果發現 Session
已失效,就會讓 Session
過期:
public void processExpires() {long timeNow = System.currentTimeMillis();Session sessions[] = findSessions();int expireHere = 0;if (log.isDebugEnabled()) {log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);}for (Session session : sessions) {if (session != null && !session.isValid()) {expireHere++;}}long timeEnd = System.currentTimeMillis();if (log.isDebugEnabled()) {log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) +" expired sessions: " + expireHere);}processingTime += (timeEnd - timeNow);}
既然懷疑這個函數可能存在定時調度,其實有一個小技巧,在這個函數上打一個斷點,看是否可能觸發,果然沒一會就觸發了:
根據 stack trace,這個函數是由 ContainerBackgroundProcessor
執行的。
Tomcat 啟動時會執行 threadStart
方法,該方法基于 java.util.concurrent.ScheduledExecutorService
啟動一個定時任務,backgroundProcessorDelay
參數可以控制啟動后多久開始執行,backgroundProcessorDelay
控制多久執行一次。
/*** Start the background thread that will periodically check for session timeouts.*/protected void threadStart() {if (backgroundProcessorDelay > 0 &&(getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState())) &&(backgroundProcessorFuture == null || backgroundProcessorFuture.isDone())) {if (backgroundProcessorFuture != null && backgroundProcessorFuture.isDone()) {// There was an error executing the scheduled task, get it and log ittry {backgroundProcessorFuture.get();} catch (InterruptedException | ExecutionException e) {log.error(sm.getString("containerBase.backgroundProcess.error"), e);}}backgroundProcessorFuture = Container.getService(this).getServer().getUtilityExecutor().scheduleWithFixedDelay(new ContainerBackgroundProcessor(), c,backgroundProcessorDelay, TimeUnit.SECONDS);}}
這表明 Tomcat 對 Session
的處理也有定期刪除機制。
總結
在 Tomcat 中,Session
的管理采用了惰性刪除和定期刪除兩種策略:
- 惰性刪除是通過
org.apache.catalina.session.StandardSession#isValid
方法實現的,每次在判斷Session
是否有效的時候,如果Session
已經無效,就會讓Session
過期(移除) - 在內存持久化策略中在內存持久化策略中,定期刪除是通過
org.apache.catalina.session.ManagerBase#processExpires
方法實現的,該方法會定期遍歷所有的Session
,對每個Session
判斷是否過期,如果發現Session
已失效,就會讓Session
過期
在 Tomcat 中,Session
的更新機制并不會直接受到 Session
過期時間設置的長短的影響,但是,如果 Session
的數量過多,可能導致 Session
清理操作的效率降低。因為在每次 Session
清理操作時,都需要遍歷所有的 Session
,檢查每個 Session
是否過期,如果過期就將其移除。如果 Session
的數量過多,那么這個過程就會消耗更多的時間和資源。
因此,雖然 Session
的過期時間設置不會直接影響 Session
的更新機制,但是 Session
的數量過多確實會對 Session
的更新效率和系統性能產生影響。為了保持系統的高效運行,應該盡量控制 Session
的數量,避免 Session
的數量過多。
歡迎關注公眾號: