Simple Session 源碼分析

摘要: 分佈式Session的實現有不少,從簡單到複雜各類各樣,可是要作到分佈式Session跟原生本地Session一致的API,對開發人員幾乎是0門檻是不容易的。SpringSession 提供了現成的分佈式Session功能,本文就是介紹SpringSession的實現細節git

1. 概要redis

本文介紹SpringSession的主要功能的實現原理。在看源碼的同時參照SpringSession開了一個「簡化」版的Session框架--SimpleSession,簡單好用,功能恰好夠用,因爲刪除了不少SpringSession種用不到的功能,源碼上可讀性更好和自定義開發更容易。spring

2. 替代本地原生Session的祕密session

幾乎全部的方案都相似,使用 Filter 把請求攔截掉而後包裝 Request 和 Response 使得 Request.getSession 返回的 Session 也是包裝過的,改變了原有 Session 的行爲,譬如存儲屬性值是把屬性值存儲在 **Redis** 中,這樣就實現了`分佈式Session`了。app

SpringSession 使用 SessionRepositoryFilter 這個過濾器來實現上面所說的。框架

SimpleSession 使用 SimpleSessionFilter 來實現。socket

2.1 SessionRepositoryFilter分佈式

包裝的類是 SessionRepositoryResponseWrapper 和 SessionRepositoryRequestWrapper 對應 Response和 Request。性能

2.2 SessionRepositoryResponseWrapper測試

繼承自 OnCommittedResponseWrapper 主要目標就是一個,當 Response 輸出完畢後調用 commit。

2.3 SessionRepositoryRequestWrapper

這個類功能比較多,由於要改變原有不少跟 Session 的接口,譬如 getSession、isRequestedSessionIdValid等。

固然最重要的是 getSession 方法,返回的 Session 是經包裝的。

2.3.1 getSession

這裏涉及到 Session 和 Repository 下面介紹。

2.3.2 commitSession

因爲如今的 Session 跟以前的已經徹底不一樣,存儲屬性值更新屬性值都是遠程操做,使用"懶操做"模式可使得頻繁的操做更加有效率。

這裏 onInvalidateSession 和 onNewSession 都是 Strategy 的方法,根據不同的策略採起的處理也不同。

Strategy 有:

  1. CookieHttpSessionStrategy
  2. HeaderHttpSessionStrategy

2.4 Session

SpringSession 的 session 有兩部分組成:

  1. Session: 接口, 默認實現類 MapSession , 它是 session 的本地對象,存儲着屬性及一些特徵(如:lastAccessedTime), 最終會被同步到遠端, 以及從遠端獲取下來後存儲在本地的實體。
  2. HttpSessionAdapter: 爲了能讓 Session 跟 HttpSession 接洽起來而設立的適配器。

2.5 Repository

各類實現,最典型用得最多的就是 RedisOperationsSessionRepository, 下面整個第3章(Spring Session Redis存儲結構)就是講整個類存儲的策略和設計。

3. Spring Session Redis存儲結構

session在存儲時分爲:

  1. session自己的一些屬性存儲
  2. 專門負責用於過時的key存儲
  3. 以時間爲key存儲在該時間點須要過時的sessionId列表

3.1 爲何須要三個存儲結構?

先說明第二存儲是用來幹嗎的,第二存儲通常設置成session的過時時間如30分鐘或者15分鐘,同時session的客戶端會註冊一個redis的key過時事件的監聽,一旦有key過時客戶端有會事件響應和處理。

在處理事件時可能會須要該session的信息,這時候第一個存儲就有用了,所以第一個存儲的過時時間會比第二存儲過時時間多1-3min,這就是爲何須要把屬性存儲和過時分開的緣由。

那第三個session的用處呢?對`Redis`比較熟悉的同窗必定會知道其中的奧祕,由於`Redis`的key過時方式是按期隨機測試是否過時和獲取時測試是否過時(也稱懶刪除),因爲按期隨機測試Task的優先級是比較低的,因此即使這個key已通過期可是沒有測試到因此不會觸發key過時的事件。因此,第三個存儲的意義在於,存儲了什麼時間點會過時的session,這樣能夠去主動請求來觸發懶刪除,以此觸發過時事件。

3.2 Redis 三個key和存儲結構

  1. Session主內容存儲,key:spring:session:sessions:{SID},內容:Map,key : value
  2. 過時存儲,key:spring:session:sessions:expires:{UUID},內容爲空
  3. 過時sessionId列表存儲,key:spring:session:expirations:{ExpiryTime},內容Set

3.3 運行方式

由於第二種 key 的存在,因此會自動失效而且發出事件,可是有延遲,因此有個定時任務在不停地掃描當前分鐘過時的 key ,即掃描第三種 key ,一旦掃描到就進行刪除。

相應事件的程序會把第一種 key 刪除。

3.4 代碼細節

3.4.1 更新失效時間

3.4.2 Redis事件監聽

當 Redis key 過時會往兩個頻道發佈事件,一個是 expired 頻道的 key 事件,一個是 key 頻道的 expired 事件。(不過須要開啓這個功能)

下面是 Spring Session 中 Redis 的事件監聽。

container.addMessageListener(messageListener,

Arrays.asList(new PatternTopic("__keyevent@*:del"),

new PatternTopic("__keyevent@*:expired")));

container.addMessageListener(messageListener, Arrays.asList(new PatternTopic(messageListener.getSessionCreatedChannelPrefix() + "*")));

事件處理:

其中,handleCreate、handleDeleted 及 handleExpired 都是用於發佈 Spring Context 的本地事件的。

4. Simple Session 簡化和優化

4.1 Session存儲使用 Map

Session主要內容在Redis中存儲採用 Map 結構能夠優化讀寫性能,由於絕大多數屬性屬於寫少讀多,若是採用總體作序列化的方式,每次都是整存整取,對於session多個屬性操做性能會略快,若是操做屬性比較少(如一個)那麼性能上會略慢,但總體上講不會對應用構成瓶頸。

4.2 修改更新

Spring Session 將對session的修改,如建立、銷燬以及put屬性都作成了在請求最後(Response Commit)再一塊兒保存到 Redis,期間隨便操做多少次都不會更新到 Redis 中,這樣確實減小了對 Redis 的操做,只要是多於一次的都是優化。(也能夠設置成每次操做都進行更新)

可是有個問題,若是 response 已經 commit 了,這時候再修改session,值將不會更新到Redis,這個也算不足。

Simple Session 對值的修改也採起懶更新或者當即更新,能夠經過配置進行切換。懶更新則使用比 Spring 更簡單的方式進行,當 SimpleSessionFilter 執行完畢之後進行提交,而不像 SpringSession 還要考慮 response 輸出完再 commit ,因此比較簡單,但若是順序排在前面的 Filter(執行 after 應該在 SimpleSessionFilter 後面)在 chain.doFilter 以後就不能再進行 session 的操做。

4.3 簡化存儲結構

3 key 式的存儲確實是設計巧妙,可是因爲 Simple Session 沒有去實現 Session 變動(create, delete & expired)事件,因此也就不必去使用 3 key 存儲。所以,使用了最簡潔的設計: SessionId -> map(key:value),存儲 Session 相關屬性及 attributes。

4.4 功能刪減

Spime Session 實現了主要功能,包括只實現了 Redis 存儲方案,至於其餘方案如:db,使用者根據須要本身按接口實現。至於如:Spring Security的支持,socket場景的支持,都沒有歸入主要功能去實現。

5. 代碼庫

碼雲:https://gitee.com/alexqdjay/simple-session

相關文章
相關標籤/搜索