分散式 WEB應用中Session(會話管理)的變遷之路
一、Session 介紹
Session 一詞直譯為 “會話”,意指有始有終的一系列動作/訊息。Session 是 Web 應用蓬勃發展的產物之一。在 Web 應用中隱含有“面向連線”和“狀態保持”兩個含義,同時也指代了 Web 伺服器與客戶端之間進行狀態保持的解決方案。
二、單體架構
早期的 Web 應用基本都採用的是單體架構,也就是把一個使用了分層架構的 Web 應用部署在單節點 Web 伺服器上。雖然採用了分層架構,將整個應用分為了表現層、業務邏輯層和資料訪問層,每一層各司其職,讓 Web 應用的各個方面都有所改善。但這樣的分層只停留於邏輯層面。由於將所有應用都部署在單個伺服器節點上,隨著應用的不斷迭代開發,單體應用將會發展成巨石應用,臃腫不堪,難以維護。
三、叢集和分散式架構
隨著 Web 應用的發展,使用者訪問量和業務複雜度與日俱進,應用的效能和程式碼的維護難度成為應用的瓶頸,為了突破瓶頸,開發者開始嘗試在應用架構中引入負載均衡器,繼而演化出了叢集和分散式兩種架構型別。
叢集:是指在多個伺服器節點上部署相同的應用,例如上圖中的伺服器B和伺服器C,然後通過負載均衡的分發功能,把使用者請求分發到叢集中的任意一個服務節點上。如果有更大的訪問量,只要向叢集中增加伺服器節點,就能改善壓力。叢集既能保證應用的高可用,又能提高應用的負載能力。
分散式:是把原來的單體架構應用,通過分而治之的手段,按照業務功能,切分成一些小的模組應用,部署在不同伺服器節點上,例如上圖中的伺服器A和伺服器B。然後通過負載均衡和門戶應用整合起來,組成一個完成的應用。
叢集和分散式架構中,後端包含了多個伺服器節點。當用戶進行登入時,登入請求是由其中一個伺服器節點進行響應,而後續的使用者請求將可能被負載均衡器分發到其他伺服器節點,這時候就可能因為這個服務節點上沒有使用者 Session ,導致伺服器判定使用者是未登陸狀態,讓使用者重新登入。所以,在叢集和分散式架構中,必須保證使用者進行登入後,架構中的所有伺服器節點都能共享 Session 資料,常用的 Session 管理方案有如下3種:
方案一:將 Session 物件儲存在 Cookie,然後存放在瀏覽器端
每次瀏覽器向伺服器傳送請求的時候,都會把整個 Session 物件放在請求裡一起傳送到伺服器,以此來實現 Session 共享。這種方法實現起來特別方便,但是由於 Cookie 的儲存容量較小,且不安全。所以這個方案只適用於 Session 數量小和安全性不高的場景。
方案二:Session 複製
部分應用伺服器能夠支援 Session 複製功能,例如 Tomcat。使用者可以通過修改配置檔案,讓應用伺服器進行 Session 複製,保持每一服務節點的 Session 資料達成一致。但是這個方案的實現依賴於應用伺服器。當應用被大量使用者訪問時,每個伺服器都需要有一部分記憶體用來存放 Session,同時因為大量 Session 通過網路傳輸進行復制,將會佔用網路資源,這可能因為網路延遲導致程式異常。
方案三:Session 粘滯
利用負載均衡的分發能力,將同一瀏覽器上同一使用者的請求,都定向傳送都固定的伺服器上,讓這個伺服器處理該使用者的所有請求,這樣只要這個伺服器上儲存的使用者 Session,就能保證使用者的狀態一致性。但是這個方案依賴於負載均衡器,而且只適用於橫向擴充套件的叢集場景,不能滿足分散式場景。同時,當指定的伺服器宕機後,會session 資訊丟失的情況。也無法利用 Nginx負載均衡,提高伺服器的利用率,而且如果 Nginx不是放在最前面一層,那麼拿到的就可能不是使用者的真實ip,那麼轉發到的伺服器就會不一樣。
1 upstream backend{ 2 server 127.0.0.1:8001; 3 server 127.0.0.1:8002; 4 ip_hash; 5 }
方案四:Session 集中管理方案選擇
這裡簡單介紹下 Spring Session。Spring Session 是 Spring 提供的一套 Session 管理方案,通過一個 SessionFilter 將所有訪問應用的請求都攔截下來,然後使用 Request 包裝類接管 Session 管理。將 Session 從Web 容器中攔截下來後,Session 會被儲存在獨立的儲存伺服器中。目前支援多種形式的 Session 儲存器:Redis、Database、MogonDB等。當 Request 進入 Web 容器,根據 Request 獲取 Session 時,由 Spring Session 負責從儲存器中獲取 Session,如果存在則返回,如果不存在則建立並持久化至儲存器中。Spring Session不與應用伺服器耦合,能使用於常規伺服器。同時還提供了瀏覽器下對同一應用儲存多個 Session 等功能。
【1】引入Spring-Session 相關 jar 包:
1 <!-- redis --> 2 <dependency> 3 <groupId>org.springframework.session</groupId> 4 <artifactId>spring-session-data-redis</artifactId> 5 <version>1.3.1.RELEASE</version> 6 <type>pom</type> 7 </dependency>
【2】 在 web.xml中,加入關於session的過濾器,只有這樣session才會被 Redis所操縱:就實現了 Redis對 session的管理。
1 <filter> 2 <filter-name>springSessionRepositoryFilter</filter-name> 3 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>springSessionRepositoryFilter</filter-name> 7 <url-pattern>/*</url-pattern> 8 </filter-mapping>