SpringSession系列-分散式 session 實現方案及 SpringSession 功能分析

glmapper發表於2019-03-03

上一篇文章 SpringSession:整合SpringBoot 中介紹瞭如何在SpringBoot中來整合 SpringSession,整個過程非常簡單,同時也簡單分析了下SpringSession的作用原理。繼上一篇實踐之後,本文主要來分析 SpringSession 的原理。

1、從 session 的一致性方案說起

關於 sessioncookie 的一些知識,大家可以參考下我之前寫的一篇文章:聊一聊session和cookie

Session作為伺服器端使用的一種記錄客戶端狀態的機制,其對客戶端是透明的;但是Session 的正常運作仍然需要客戶端瀏覽器的支援。我們都知道,HTTP協議是無狀態的,Session不能依據HTTP連線來判斷是否為同一客戶,因此伺服器需要向客戶端瀏覽器傳送一個識別標誌(sessionId),這個識別標誌通過是通過Cookie機制來完成。

1.1、session 一致性問題的由來

當使用者首次訪問我們的Servlet時,應用伺服器端會給使用者建立一個獨立的Session,並且儲存在記憶體中。這種情況在單應用伺服器場景下是可以滿足的(這裡不討論其一個弊端,就是記憶體佔用給伺服器帶來的壓力的問題)。在叢集場景下,這種機制就會到來問題:

1.1.1、單機場景

SpringSession系列-分散式 session 實現方案及 SpringSession 功能分析

因為是一臺應用伺服器,使用者的每次請求都是由這臺機器來處理,所以不會有session共享問題。

1.1.2、叢集場景

SpringSession系列-分散式 session 實現方案及 SpringSession 功能分析

假設現在叢集中有三臺機器,(從上到下:A->B->C)。當前使用者首次發起訪問時,請求被分配到 A 機器處理,Session資料被寫入 A 機器的記憶體中;當再次發起訪問 時,請求被分配的 B 處理,但此時 B 記憶體中並沒有當前使用者的任何資料,這樣就出現了session不一致的情況了。

1.2、Session 一致性問題的方案

對於當前服務化、單元化應用盛行的時代,簡單的記憶體型的 Session 已經不能夠滿足我們的要求了。那麼我們就需要尋求一種方案來替換目前單機記憶體儲存實現的方案。

1.2.1 基於 IP-HASH 的實現機制

在 1.1.2 中因為我們無法知道請求會被分配到哪臺機器來處理,所以會導致session不一致的問題出現。如果我們可以解決讓每個使用者的請求能夠固定的打到某一臺機器上,那麼上面提到的問題其實也就不存在了。IP-HASH 就是這樣一種方案。通過對請求的客戶端 IP 進行 HASH 計算,並將計算結果對映到具體一臺機器,這樣就可以將請求固定分配到某一臺機器上,從而有效的避免session一致性問題的出現。

這種方案的好處在於:

  • 不需要修改任何應用程式碼,0 侵入。
  • 安全性高,不依賴其他三方快取框架帶來的風險
  • 成本低

但是問題也很明顯,這種方式實際上是規避了session一致性問題的出現,並非是針對session一致性問題給出的解決方案。主要問題:

  • 基於應用記憶體,會給應用伺服器帶來一定的壓力
  • 服務重啟會導致session資料丟失
  • 不利於水平擴充套件,水平擴充套件也可能丟失session
  • 存在單點負載高的情況,就是多數請求經過HASH計算之後打到同一臺機器,而其他機器處於空閒狀態。

1.2.2 session 複製

這種方式的實現原理是應用伺服器建立session之後通過組播的方式將session傳送到組播地址內的其他應用伺服器上。這種方式相較於IP-HASH 的方式要靠譜一點:

  • 同樣不需要更改任何業務程式碼
  • 能夠適應多種負載策略
  • 機器重啟或者當機之後不怕丟失,因為有冗餘備份

但是這種方式也有比較大的問題:

  • 首先就是伺服器之間同步session會佔用一定的網路資源,同時session在不同的機器之間進行同步存在延遲。
  • 還是基於記憶體儲存,侷限於機器記憶體容量影響,水平擴充套件能力差
  • 伺服器記憶體因為需要儲存其他機器上的session資料,對記憶體的消耗會隨著叢集的規模變大而變大,可能會導致機器頻繁觸發GC

1.2.3 藉助三方快取框架實現 session 集中管理

上面兩種方式都是有伺服器自己來管理session的,主要問題還是在於對於效能和記憶體的影響。而這種方式的原理是將session託管給三方軟體(如redis)來統一管理。這種方式可以有效的解決效能、記憶體佔用以及水平擴充套件等問題。但是因為引入了三方軟體,在實現複雜度、運維成本等方面會有所增加。

目前所接觸到的分散式session的實現方案,大多都是基於這種方式來實現的;SpringSession 也不例外。

2、SpringSession 功能結構分析

前面對分散式場景下的 Session一致性問題進行了說明,並對解決Session一致性的問題的幾種策略進行的分析(有點糙,網上這些知識有很多)。在瞭解這些背景之後,我們來看下 SpringSession 的實現原理。

2.1 簡介

Spring Session 提供了用於管理使用者會話資訊的API和實現,在不依賴特定於應用程式容器的解決方案的情況下,使得支援群集會話變得更加簡單。它還提供了透明的整合:

  • 允許以應用程式容器(Tomcat等)中立的方式替換 HttpSesseion,支援在 headers中提供 session IDs來使用 RESTful API
  • 提供在接收 WebSocket 訊息時保持HTTP 會話存活的能力
  • 允許以應用程式容器中立的方式替換 Spring WebFluxWebSession

以上來自官網文件翻譯 Spring Session

2.2 模組

Spring Session 主要包括 4 個模組:

  • spring-session-core :提供了 Spring Session 核心功能和API
  • spring-session-data-redis:以 redis 作為儲存機制的 SessionRepository 實現
  • spring-session-hazelcast:以 Hazelcast 作為儲存機制的 SessionRepository 實現
  • spring-session-jdbc:以關係型資料庫作為儲存機制的 SessionRepository 實現

總體來說就是 核心API+儲存實現;工程模組截圖如下:

SpringSession系列-分散式 session 實現方案及 SpringSession 功能分析

2.3 功能結構

SpringSession整體上可以分為三塊:

  • 對於Web層的處理,這裡包括對於請求的重寫,自定義的filter加入到filter chain,cookie處理,http header處理等
  • 公共基礎封裝,比如儲存類的頂層抽象介面定義,自定配置,事件處理等。
  • 儲存部分,這部分實際上是對公共基礎封裝介面的實現,提供了豐富的儲存實現,包括redis,記憶體儲存,jdbc等。

2.4 多 session 支援

對於常用的分散式session,在實現上一般會依賴於 cookie。但是在 springsession 中提供了基於header來傳遞jessionID的策略實現。同時在 2.0.4 版本之前,對於同一個瀏覽器同一個網站,springsession 支援多個session問題,但是在此版本之後拋棄了對於對 session 的支援。關於更多關於多session支援可以檢視 SpringSession 的官方文件

小結

本文對分散式 session 的幾種實現策略進行了簡單的介紹。對於分散式 session 而言,如何解決一致性問題是關鍵,目前我見過的絕大多數方案均是以 【藉助三方快取框架實現 session 集中管理】 這種來實現的,包括本系列文章中所要介紹的 SpringSession。

除分散式session一致性方式解決方案的介紹之外,作為SpringSession 的第二篇文章,在這裡簡單分析了下Springsession的功能模組,以便後續展開對原始碼的分析。

相關文章