單點登入解決方案
本文將從以下幾個方面介紹單點登入問題,和單點登入的解決方案:
- http 協議的特性
- 叢集環境下的 session 共享問題
- 關於負載均衡演算法分析
- session 共享問題的解決辦法
http 協議
無狀態性
Http 協議本身是無狀態的,客戶端只需簡單的向伺服器傳送請求,獲取結果。無論是客戶端還是伺服器都沒必要記錄彼此過去的行為,每一次請求之間都是獨立的。
然而我們發現如果能夠提供一些按照需要生成的動態資訊會使web訪問變得更加便捷。比如我們做了一個需要登入授權才能執行的動作,但是因為 http 協議沒辦法儲存這個登入的使用者的狀態,因此當下一次再執行一個需要授權操作時,還需要再次登入,這將導致使用者體驗非常差。因此需要一種能夠識別每次使用者的請求的機制,來實現會話儲存的目的。
Session
服務端提供了一種叫 Session 的機制,對於每個使用者的請求,會生成一個唯一的標識。當程式需要為某個客戶端的請求建立一個 session 的時候,伺服器首先檢查這個客戶端的請求是否包含了一個 session 標識(session id)。如果已包含一個 session id 則說明以前已經為客戶端建立過 session,伺服器就按照 session id 把這個 session 檢索出來使用。
如果客戶端請求不包含 session id,則為此客戶端建立一個session 並且生成一個與此 session 相關聯的 session id,session id 的值是一個既不會重複,又不容易被找到規律的字串。
Cookie
瀏覽器提供了一種叫 cookie 的機制,儲存當前會話的唯一標識。每次 HTTP 請求,客戶端都會發送相應的 cookie 資訊到服務端。客戶端第一次請求,由於 cookie 中並沒有攜帶 session id,服務端會建立一個session id,並寫入到客戶端的 cookie 中。以後每次請求,客戶端都會攜帶這個 id 發給伺服器端。這樣一來,便解決了無狀態的問題。

互動圖
叢集環境下的 session 共享問題
如果網站請求流量較大,那麼單臺伺服器是無法承接這些流量的,這個時候就需要開始對伺服器做叢集。

採用了多臺機器做叢集以後,就勢必要通過一種機制來實現請求的路由。因為對於使用者來說,訪問的是一個域名,至於後端應該請求到哪一臺,使用者並不需要關心,所以這裡會使用負載均衡裝置。
關於負載均衡
負載均衡的主要目的是:把使用者的請求分發到多臺後端的裝置上,用以均衡伺服器的負載。我們可以把負載均衡器劃分為兩大類:硬體負載均衡器和軟體負載均衡器。
硬體負載
最常用的硬體負載裝置有 F5 和 netscaler、redware。其中F5是基於4層負載,netscaler是7層負載。所謂的四到七層負載均衡,就是在對後臺的伺服器進行負載均衡時,依據四層的資訊或七層的資訊來決定怎麼樣轉發流量。
四層的負載均衡,就是通過釋出三層的 IP 地址,然後加四層的埠號,來決定哪些流量需要做負載均衡,轉發至後臺伺服器,並記錄下這個 TCP 或者 UDP 的流量是由哪臺伺服器處理的,後續這個連線的所有流量都同樣轉發到同一臺伺服器處理。同理,還有基於MAC地址的二層負載均衡和基於IP地址的三層負載均衡。
七層的負載均衡,就是在四層的基礎上(沒有四層是絕對不可能有七層的),再考慮應用層的特徵,比如同一個 Web 伺服器的負載均衡,除了根據IP加埠辨別是否需要處理的流量,還可根據七層的 URL、瀏覽器類別、語言等資訊來決定是否要進行負載均衡。舉個例子,如果你的 Web 伺服器分成兩組,一組是中文語言的,一組是英文語言的,那麼七層負載均衡就可以當用戶來訪問你的域名時,自動辨別使用者語言,然後選擇對應的語言伺服器組進行負載均衡處理。
硬體負載均衡裝置的優點是穩定性高、同時有專門的技術服務團隊支撐。但是價格比較貴,一般的都要幾十萬起。對於一些大的企業,如果沒有專業的運維團隊,可以採用這種方式。
軟體負載
比較主流的開源軟體負載技術有: lvs、HAProxy、Nginx 等。對於小公司來說或者大型的網際網路企業,基本都採用軟體負載均衡技術來實現流量均衡。不同的負載均衡技術有不同的特點,比如 LVS 是基於 4 層的負載負載技術,抗負載能力比較強。HAProxy 和 Nginx 是基於 7 層的負載均衡技術,可以根據請求的 url 進行分流。
負載均衡演算法
引入負載均衡器以後,就需要負載均衡演算法對請求進行轉發,常見的負載均衡演算法有以下幾種:
輪詢演算法及加權輪詢演算法
輪詢演算法是指負載均衡伺服器將客戶端請求按順序輪流分配到後端伺服器上,以達到負載均衡的目的。由於後端伺服器的效能差異,可以對處理能力較好的伺服器增加權重。這樣,效能好的伺服器能處理更多的任務,效能較差的伺服器處理較少的任務。
最小連線數
由於不同的客戶端請求的操作對於後端來說複雜度是不同的,也就會導致服務端的處理時間不一樣。最小連線數演算法根據後端伺服器當前的連線數情況,動態地選取其中積壓連線數最小的一臺伺服器來處理當前的請求,儘可能提高後端伺服器的利用效率,合理地將請求分流到每一臺伺服器。
隨機演算法
隨機演算法是指隨機選擇一臺後端伺服器進行請求的處理。由於每次伺服器被挑中的概率都一樣,客戶端的請求可以被均勻地分派到所有的後端伺服器上。
雜湊演算法
獲取客戶端的 IP 地址,通過雜湊函式計算得到的一個數值,用該數值對伺服器列表的大小進行取模運算,得到的結果便是客服端要訪問伺服器的序號。採用源地址雜湊法進行負載均衡,同一IP地址的客戶端,當後端伺服器列表不變時,它每次都會對映到同一臺後端伺服器進行訪問。
Session 共享問題的解決方案
Session 共享問題,已經有非常多的解決方案,主流的方案有以下幾種。
session sticky
session sticky(粘性) , 保證同一個會話的請求都在同一個web 伺服器上處理,這樣的話,就完全不需要考慮會話的問題。比如前面說的負載均衡演算法中,雜湊演算法就是一個典型的實現手段。
這種實現方式會有些問題:
1、如果 web 伺服器宕機或者重啟,那麼這臺機器上儲存的會話資料都會丟失,會造成使用者暫時無法訪問的問題,或者使用者之前的授權操作需要再執行一次。
2、 通過這種方式實現保持session,沒有辦法進行4層網路轉發,只能在7層網路上進行解析並轉發。
session replication
session複製,通過相關技術實現 session 複製,使得叢集中的各個伺服器相互儲存各自節點儲存的 session 資料。tomcat 本身就可以基於 IP 組播的方式,實現 session 複製的功能,
這種實現方式的問題:
- 同步 session 資料會造成網路開銷,隨著叢集規模越大,同步 session 帶來的頻寬影響也越大。
- 每個節點都需要儲存叢集中所有節點的 session 資料,對記憶體的開銷大。
session 統一儲存
叢集中的各個節點的session 資料,統一儲存到一個儲存裝置中。每個節點去拿session 的時候,都從相應的第三方儲存中去拿。對於這個方案來說,無論是哪個節點新增或者修改了session 資料,最終都會發生在這個集中儲存的地方。這個儲存裝置可以是redis、也可以是mysql。

這種實現方式的問題:
1、讀寫session 資料需要進行網路操作,存在不穩定性和延遲性
2、如果儲存session 的伺服器出現故障,將大規模的影響到應用
Cookie Based
Cookie Based 方法,簡單來說,就是不依賴容器本身的Session 機制。而是服務端基於一定的演算法,生成一個token 給到客戶端,客戶端每次請求,都會攜帶這個token。當服務端收到token 以後,先驗證token 是否有效,再解密這個token 獲取關鍵資料進行處理。
Cookie Based 實現方式
基於純Cookie 的方式,也就是客戶端每次請求都攜帶身份資訊給到服務端。比較典型的方式是JWT,全稱是JSON Web Tokens。是一種簡潔的並且在兩個計算機之間安全傳遞資訊的表述性宣告規範。JWT 的宣告一般被用來在客戶端和服務端之間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源。比如用在使用者登入上。
JWT 強調的是服務端不對token進行儲存,而是直接通過簽名演算法驗證並解密token 得到相應資料進行處理。

JWT 的互動過程