Java常用包系列--SpringSession(原理)
技術標籤:專案(後端)
其他網址
分散式Session系列--綜述_分散式_feiying0canglang的部落格-CSDN部落格
第十七節 SpringBoot通過Redis實現Session共享_大宇的部落格,歡迎訪問-CSDN部落格
springboot整合redis和springsession做session伺服器_請叫我猿叔叔的部落格-CSDN部落格
SpringBoot 2 整合 Spring Session 最簡操作_ljk126wy的部落格-CSDN部落格
spring boot + redis 實現session共享分析 - 簡書
簡介
簡介
Spring-Session原理:設計一個Filter,利用HttpServletRequestWrapper,實現自己的 getSession()方法,接管建立和管理Session資料的工作。
Redis儲存Session不僅僅可以用Spring-Session,還可以用Shiro引入。Shiro+Redis用法見:Shiro應用系列--快取_feiying0canglang的部落格-CSDN部落格
請求攔截
Spring Session 是通過SessionRepositoryFilter過濾器進行攔截,然後通過SessionRepositoryRequestWrapper繼承HttpServletRequestWrapper進行管理Session。
存放策略
Spring Session 為我們提供了3中存放的策略而每種策略提供對應的註解啟動。分別為:
(1)NoSql形式的MongoDb:@EnableMongoHttpSession
(2)持久化形式的JDBC:@EnableJdbcHttpSession
(3)快取形式的Redis:@EnableRedisHttpSession
Spring Session:共享Session過程
1.過濾請求
Spring-Session自動注入springSessionRepositoryFilter過濾器,每次請求都過濾。其本質是:對每個請求的request進行一次封裝。request.getSession()的時候,實際上拿到是Spring封裝後的session,這個session儲存在Redis中。
2.應用獲取session
應用通過 getSession(boolean create) 方法來獲取 session 資料,引數 create 表示 session 不存在時是否建立新的 session 。 getSession 方法首先從請求的 “.CURRENT_SESSION” 屬性來獲取 currentSession 。
3.若session不存在
沒有 currentSession ,則從 request 取出 sessionId ,然後讀取 spring:session:sessions:[sessionId] 的值,同時根據 lastAccessedTime 和 MaxInactiveIntervalInSeconds 來判斷這個 session 是否過期。如果 request 中沒有 sessionId ,說明該使用者是第一次訪問,會根據不同的實現,如 RedisSession ,MongoExpiringSession ,GemFireSession 等來建立一個新的 session ,新增到請求中。
從 request 取 sessionId 依賴具體的 HttpSessionStrategy 的實現,Spring Session 給了兩個預設的實現 CookieHttpSessionStrategy 和 HeaderHttpSessionStrategy ,即從 cookie 和 header 中取出 sessionId 。
整體流程
Spring-Session會自動注入springSessionRepositoryFilter過濾器,每一次的請求都由他來過濾,其本質是:對每一個請求的request進行了一次封裝。request.getSession()的時候,實際上拿到是Spring封裝後的session,這個session儲存在Redis資料庫中。
應用通過 getSession(boolean create) 方法來獲取 session 資料,引數 create 表示 session 不存在時是否建立新的 session 。 getSession 方法首先從請求的 “.CURRENT_SESSION” 屬性來獲取 currentSession ,沒有 currentSession ,則從 request 取出 sessionId ,然後讀取 spring:session:sessions:[sessionId] 的值,同時根據 lastAccessedTime 和 MaxInactiveIntervalInSeconds 來判斷這個 session 是否過期。
如果 request 中沒有 sessionId ,說明該使用者是第一次訪問,會根據不同的實現,如 RedisSession ,MongoExpiringSession ,GemFireSession 等來建立一個新的 session,新增到請求中 。
從 request 取 sessionId 依賴具體的 HttpSessionStrategy 的實現,Spring Session 給了兩個預設的實現 CookieHttpSessionStrategy 和 HeaderHttpSessionStrategy ,即從 cookie 和 header 中取出 sessionId 。
Spring-Session在Redis中的儲存結構
spring:session是預設的Redis HttpSession字首(Redis中,常用’:’分割)。如上圖,每個session都建立3組資料:
(1)session本身的一些屬性(spring:session:sessions:9ab7456f-4882-40c1-b2c5-929f98395234)
hash結構,儲存:creationTime、maxInactiveInterval、、lastAccessedTime、session資訊(實體類的序列化資料)。
(2)會過期的key(spring:session:sessions:expires:9ab7456f-4882-40c1-b2c5-929f98395234)
string結構,value為空。
(3)以時間為key儲存在該時間點過期的sessionId列表(spring:session:expirations:1599909240000:)
set結構,儲存在這個時間過期的sessionId
session儲存結構總結
session在儲存時分為三個結構:
- session本身的一些屬性儲存
- 會過期的key
- 以時間為key儲存在該時間點需要過期的sessionId列表
為什麼需要三個儲存結構?
先說明第2個結構是用來幹嘛的,第二儲存一般設定成session的過期時間如30分鐘或者15分鐘,同時session的客戶端會註冊一個redis的key過期事件的監聽,一旦有key過期客戶端有會事件響應和處理。
在處理事件時可能會需要該session的資訊,這時候第1個結構就有用了,因此第1個結構的過期時間會比第二儲存過期時間多1-3min,這就是為什麼需要把屬性儲存和過期分開的原因。那第3個結構的用處呢?對`Redis`比較熟悉的同學一定會知道其中的奧祕,因為`Redis`的key過期方式是定期隨機測試是否過期和獲取時測試是否過期(也稱懶刪除),由於定期隨機測試Task的優先順序是比較低的,所以即便這個key已經過期但是沒有測試到所以不會觸發key過期的事件。所以,第3個結構的用處的意義在於,儲存了什麼時間點會過期的session,這樣可以去主動請求來觸發懶刪除,以此觸發過期事件。
原始碼分析
其他網址
spring boot + redis 實現session共享分析 - 簡書
SpringSession系列-整合SpringBoot - 掘金
Spring-Session的Key
Spring-Session是使用預設的UUID來作為Key值來將Session儲存到Redis容器中的。
相關程式碼
// ---------- RedisSession的建構函式 RedisSession() { // 關鍵就是下面這句了, RedisSession以一個MapSession作為backup. // 另外JdbcSession也是以一個MapSession作為backup. this(new MapSession()); this.delta.put(CREATION_TIME_ATTR, getCreationTime()); this.delta.put(MAX_INACTIVE_ATTR, getMaxInactiveIntervalInSeconds()); this.delta.put(LAST_ACCESSED_ATTR, getLastAccessedTime()); this.isNew = true; flushImmediateIfNecessary(); } // ---------- MapSession的建構函式 public MapSession() { // 這裡的UUID.randomUUID().toString()就是預設的Redis中的鍵值; // 當然其實Spring-Session將Session存入Redis時會給這個值附加一些前後綴. 不過與我們本次關注的重點關係不大. 就不再深入講解了. this(UUID.randomUUID().toString()); } public MapSession(String id) { // 所以上面生成的uuid最終是被儲存在mapSession的id欄位裡. this.id = id; }