當前位置:
首頁 > 最新 > 分散式共享 Session之SpringSession 源碼細節

分散式共享 Session之SpringSession 源碼細節

#掃描上方二維碼進入報名#

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

1. 概要

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

2. 替代本地原生Session的秘密

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

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

SimpleSession 使用 SimpleSessionFilter 來實現。

2.1 SessionRepositoryFilter

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

2.2 SessionRepositoryResponseWrapper

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


這個類功能比較多,因為要改變原有很多跟 Session 的介面,譬如 getSession、isRequestedSessionIdValid等。

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

2.3.1 getSession

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

2.3.2 commitSession

由於現在的 Session 跟之前的已經完全不同,存儲屬性值更新屬性值都是遠程操作,使用"懶操作"模式可以使得頻繁的操作更加有效率。

這裡 onInvalidateSession 和 onNewSession 都是 Strategy 的方法,根據不一樣的策略採取的處理也不一樣。

Strategy 有:

CookieHttpSessionStrategy

HeaderHttpSessionStrategy


SpringSession 的 session 有兩部分組成:

Session: 介面, 默認實現類 MapSession , 它是 session 的本地對象,存儲著屬性及一些特徵(如:lastAccessedTime), 最終會被同步到遠端, 以及從遠端獲取下來後存儲在本地的實體。

HttpSessionAdapter: 為了能讓 Session 跟 HttpSession 接洽起來而設立的適配器。


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

3. Spring Session Redis存儲結構

session在存儲時分為:

session本身的一些屬性存儲

專門負責用於過期的key存儲

以時間為key存儲在該時間點需要過期的sessionId列表


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

在處理事件時可能會需要該session的信息,這時候第一個存儲就有用了,因此第一個存儲的過期時間會比第二存儲過期時間多1-3min,這就是為什麼需要把屬性存儲和過期分開的原因。

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

3.2 Redis 三個key和存儲結構

Session主內容存儲,key:spring:session:sessions:,內容:Map,key : value

過期存儲,key:spring:session:sessions:expires:,內容為空

過期sessionId列表存儲,key:spring:session:expirations:,內容Set


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

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

3.4 代碼細節

3.4.1 更新失效時間


當 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

github: https://github.com/alexqdjay/simple-session

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 開源中國 的精彩文章:

使用 Angular 5.0和Spring Boot 2.0 構建一個基本的 CRUD 應用
谷歌正式發布 TensorFlow 1.5,終於支持 CUDA9和cuDNN7

TAG:開源中國 |