前言
偶然發現Tomcat會話時間的半小時,并不是說會話創建后,只有半小時的有效使用時間,而是說會話空閑半小時后,會被刪除。索性就翻了一下源碼。做了一番整理。
注:空閑時間,指的是同一個會話兩次請求之間的間隔時間
Session相關類圖

- HttpSession就是大家Servlet層可以直接使用的Session.
- Session是Tomcat內部使用的接口,可以做一些內部調用
- StandardSession是標準的HttpSession實現,同時它也實現了Session接口,用于Tomcat內部管理
- StandardSessionFacade,類名已經指明它就是一個“門面類”,它內部會引用一個StandardSession的對象,但對外只提供HttpSession規定的方法。
Manager相關類圖

StandardManager與PersitentManager都是Manager的實現,但是它們在存儲Session對象的方式上有所不同。
StandarManager
1.Tomcat運行時,把Session存儲在內存中
2.Tomcat關閉時(注意是正常的關閉操作,而非突然崩潰),會把Session寫入到磁盤中,等到Tomcat重啟后再把Session加載進來
PersistentManager
1.總是把Session存儲在磁盤中。
Manager與Context的關系
在Tomcat中,一個Context就是部署到Tomcat中的一個應用(Webapp)。每一個Context都有一個單獨的Manager對象來管理這個應用的會話信息。

Manager如何存儲Session
Manager對象會使用一個Map來存儲Session對象
- Key => SessionId
- Value => Session Object
/**
* The set of currently active Sessions for this Manager, keyed by
* session identifier.
*/
protected Map<String, Session> sessions = new ConcurrentHashMap<>();
當一個請求到達Context的時候,如果它帶有JSESSIONID的Cookie,Manager就能依此找到關聯的Session對象,放入到Request對象中。
Manager的定期檢查
Manager接口有一個backgroundProcess()方法,顧名思義就是后臺處理。
/**
* This method will be invoked by the context/container on a periodic
* basis and allows the manager to implement
* a method that executes periodic tasks, such as expiring sessions etc.
*/
public void backgroundProcess();
注:Container接口也有這個方法,這個方法一般在容器啟動(start)的時候,開啟一個額外的線程來執行這個backgroundProcess方法。其中Context的這個方法啟動后,會執行Loader和Manager的backgroundProcess方法。
我們來看看這個方法都做了些什么?
/**
* {@inheritDoc}
* <p>
* Direct call to {@link #processExpires()}
*/
@Override
public void backgroundProcess() {
count = (count + 1) % processExpiresFrequency;
if (count == 0) //如果達到檢查頻率則開始檢查
processExpires();
}
/**
* Invalidate all sessions that have expired.
*/
public void processExpires() {
long timeNow = System.currentTimeMillis();
Session sessions[] = findSessions(); //獲取所有session對象
int expireHere = 0 ; //過期session的數量,不要被這個變量名騙了
if(log.isDebugEnabled())
log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length);
for (int i = 0; i < sessions.length; i++) {
if (sessions[i]!=null && !sessions[i].isValid()) {
expireHere++;
}
}
long timeEnd = System.currentTimeMillis();
if(log.isDebugEnabled()) //打印記錄
log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere);
processingTime += ( timeEnd - timeNow );
}
很多人看到這里,可能會有跟我一樣的疑惑,即這里面根本就沒有使Session過期失效的操作,好像只做了狀態檢查。不過后來看到了Session的isValid方法的實現就都明白了。
/**
* Return the <code>isValid</code> flag for this session.
*/
@Override
public boolean isValid() {
if (!this.isValid) {
return false;
}
if (this.expiring) {
return true;
}
if (ACTIVITY_CHECK && accessCount.get() > 0) {
return true;
}
//關鍵所在
//如果有設置最大空閑時間
//就獲取此Session的空閑時間進行判斷
//如果已超時,則執行expire操作
if (maxInactiveInterval > 0) {
int timeIdle = (int) (getIdleTimeInternal() / 1000L);
if (timeIdle >= maxInactiveInterval) {
expire(true);
}
}
return this.isValid;
}
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。