會話保持原理

1. 什麼是會話保持?

會話保持是負載均衡最常見的問題之一,也是一個相對比較複雜的問題。會話保持有時候又叫做粘滯會話(Sticky Sessions)。會話保持是指在負載均衡器上的一種機制,可以識別客戶端與服務器之間交互過程的關連性,在作負載均衡的同時還保證一系列相關連的訪問請求會分配到一臺服務器上


2. 什麼是Session

在WEB開發中,服務器可以爲每個用戶瀏覽器創建一個會話對象(session對象),注意:一個瀏覽器獨佔一個session對象(默認情況下)。因此,在需要保存用戶數據時,服務器程序可以把用戶數據寫到用戶瀏覽器獨佔的session中,當用戶使用瀏覽器訪問其它程序時,其它程序可以從用戶的session中取出該用戶的數據,爲用戶服務。

2.1 Connection與Session的區別

在討論這個問題前,我們必須先花點時間弄清楚一些概念:什麼是連接(Connection)、什麼是會話(Session),以及這二者之間的區別。需要特別強調的是,如果我們僅僅是談論負載均衡,會話和連接往往具有相同的含義。

從簡單的角度來看,如果用戶需要登錄,那麼就可以簡單的理解爲會話;如果不需要登錄,那麼就是連接。


2.2 Cookie與Session的區別

  • Cookie是把用戶的數據寫給用戶的瀏覽器。
  • Session保存在服務端。

2.3 Session的原理

2.3.1 基於Cookie實現

服務器創建session出來後,會把session的id號,以cookie的形式回寫給客戶機,這樣,只要客戶機的瀏覽器不關,再去訪問服務器時,都會帶着session的id號去,服務器發現客戶機瀏覽器帶session id過來了,就會使用內存中與之對應的session爲之服務。可以用如下的代碼證明:

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionDemo1 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setCharacterEncoding("UTF=8");
        response.setContentType("text/html;charset=UTF-8");
        //使用request對象的getSession()獲取session,如果session不存在則創建一個
        HttpSession session = request.getSession();
        //將數據存儲到session中
        session.setAttribute("data", "孤傲蒼狼");
        //獲取session的Id
        String sessionId = session.getId();
        //判斷session是不是新創建的
        if (session.isNew()) {
            response.getWriter().print("session創建成功,session的id是:"+sessionId);
        }else {
            response.getWriter().print("服務器已經存在該session了,session的id是:"+sessionId);
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

第一次訪問時,服務器會創建一個新的sesion,並且把session的Id以cookie的形式發送給客戶端瀏覽器,如下圖所示:

這裏寫圖片描述

點擊刷新按鈕,再次請求服務器,此時就可以看到瀏覽器再請求服務器時,會把存儲到cookie中的session的Id一起傳遞到服務器端了,如下圖所示:

這裏寫圖片描述


2.3.2 基於URL重寫實現

所謂URL重寫,顧名思義就是重寫URL。試想,在返回用戶請求的頁面之前,將頁面內所有的URL後面全部以get參數的方式加上session標識符(或者加在path info部分等等),這樣用戶在收到響應之後,無論點擊哪個鏈接或提交表單,都會在再帶上session的標識符,從而就實現了會話的保持。讀者可能會覺得這種做法比較麻煩,確實是這樣,但是,如果客戶端禁用了cookie的話,URL重寫將會是首選:

  • response.encodeRedirectURL(java.lang.String url) 用於對sendRedirect方法後的url地址進行重寫
  • response.encodeURL(java.lang.String url)用於對錶單action和超鏈接的url地址進行重寫

先禁用瀏覽器的cookie功能,再查看返回頁面html代碼可以看到,每一個超鏈接後面都帶上了session的Id,如下所示:

本網站有如下書:<br/>javaweb開發   <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=1'>購買</a><br/>
spring開發   <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=2'>購買</a><br/>
hibernate開發   <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=3'>購買</a><br/>
struts開發   <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=4'>購買</a><br/>
ajax開發   <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=5'>購買</a><br/>

所以,當瀏覽器禁用了cookie後,就可以用URL重寫這種解決方案解決Session數據共享問題。而且response. encodeRedirectURL(java.lang.String url)response. encodeURL(java.lang.String url)兩個非常智能的方法,當檢測到瀏覽器沒有禁用cookie時,那麼就不進行URL重寫了


3. 什麼時候需要會話保持?

比如在某些要求登錄狀態的情境下,要求客戶端和服務器之間保持一個會話(session)以記錄客戶端的各種信息。比如在大多數電子商務的應用系統或者需要進行用戶身份認證的在線系統中,一個客戶與服務器經常經過好幾次的交互過程才能完成一筆交易或者是一個請求的完成。由於這幾次交互過程是密切相關的,服務器在進行這些交互過程的某一個交互步驟時往往需要了解上一次或上幾次的交互過程處理結果,這就要求所有這些相關的交互過程都由一臺服務器完成,而不能被負載均衡器分散到不同的服務器上。否則可能出現異常情景:

  • 客戶端輸入了正確的用戶名和口令,但卻反覆跳到登錄頁面;
  • 用戶輸入了正確的驗證碼,但是總提示驗證碼錯誤;
  • 客戶端放入購物車的物品丟失

因此會話保持機制的意義就在於,確保在合適的情境下,將來自相同客戶端的請求轉發至後端相同的服務器進行處理。換句話說,就是將客戶端與服務器之間建立的多個連接,都發送到相同的服務器進行處理。如果在客戶端和服務器之間部署了負載均衡設備,很有可能這多個連接會被轉發至不同的服務器進行處理。如果服務器之間沒有會話信息的同步機制,會導致其他服務器無法識別用戶身份,造成用戶在和應用系統發生交互時出現異常。


4. 會話保持的分類

4.1 基於源地址的會話保持

基於源地址的會話保持/基於IP的會話保持 是指負載均衡器在作負載均衡時根據訪問請求的源地址作爲判斷關連會話的依據。對來自同一IP地址的所有訪問請求在作負載均時都會被保持到一臺服務器上去。

簡單會話保持中一個很重要的參數就是連接超時值,負載均衡器會爲每一個處於保持狀態中的會話設定一個時間值。若一個會話從上一次完成到下次再來之間的間隔時間小於超時值時,負載均衡器將會將新的連接進行會話保持;但如果這個間隔大於該超時值,負載均衡器會將新來的連接認爲是新的會話然後進行負載平衡。

這裏寫圖片描述

簡單會話保持實現簡單,只需要根據數據包三、四層的信息就可以實現,效率比較高。

缺點:

  • 但此種方式存在的問題就在於,當多個客戶端通過代理地址轉換的方式訪問服務器時,由於來源地址一樣,請求都被分配到同一臺服務器上,會導致服務器之間的負載嚴重失衡。
  • 另外一種情況是,同一個客戶端產生大量併發,要求分配到多個服務器上處理的同時進行會話保持。這時基於客戶端源地址的會話保持方法也會導致負載均衡失效。

以上情況出現時,就必須要考慮使用其他的會話保持方式。


4.2 共享session的會話保持

此種方式通過多個後端服務器共享session的方式,實現與負載均衡同時的會話保持。主要有以下幾種形式:

(1)數據庫存放

Session信息存儲到數據庫表以實現不同應用服務器間Session信息的共享。此種方式適合數據庫訪問量不大的網站。

  • 優點:實現簡單
  • 缺點:由於數據庫服務器相對於應用服務器更難擴展且資源更爲寶貴,在高併發的Web應用中,最大的性能瓶頸通常出現在數據庫服務器。因此如果將 Session存儲到數據庫表,頻繁的數據庫操作會影響業務。

(2) 文件系統存放

通過文件系統(比如NFS)來實現各臺服務器間的Session共享。此種方式適合併發量不大的網站。

  • 優點:各臺服務器只需要mount存儲Session的磁盤即可,實現較爲簡單。
  • 缺點:NFS對高併發讀寫的性能並不高,在硬盤I/O性能和網絡帶寬上存在較大瓶頸,尤其是對於Session這樣的小文件的頻繁讀寫操作。

(3) Memcached/Redis存放

利用Memcached來保存Session數據,直接通過內存的方式讀取。

  • 優點:效率高,在讀寫速度上會比存放在文件系統時快很多,而且多個服務器共用Session也更加方便,將這些服務器都配置成使用同一組memcached服務器就可以,減少了額外的工作量。
  • 缺點:一旦宕機內存中的數據將會丟失,但對Session數據來說並不是嚴重的問題。如果網站訪問量太大、Session太多的時候memcached會將不常用的部分刪除,但是如果用戶隔離了一段時間之後繼續使用,將會發生讀取失敗的問題。

4.3 基於cookie的會話保持

在基於cookie模式下負載均衡器負責插入cookie,後端服務器無需作出任何修改。當客戶端進行第一次請求時,客戶端的HTTP request(不帶cookie)進入負載均衡器, LB根據負載平衡算法策略選擇後端一臺服務器,並將請求發送至該服務器;後端服務器的HTTP response(不帶cookie)被髮回給負載均衡器。接下來負載均衡器將向Response插入Cookie,並將HTTP response返回到客戶端。

當客戶請求再次發生時,客戶HTTP request(帶有上次負載均衡器插入的cookie)進入LB,然後LB讀出cookie裏的會話保持數值,將HTTP request(帶有與上面同樣的cookie)發到指定的服務器,然後後端服務器進行請求回覆;由於服務器並不寫入cookie,HTTP response將不帶cookie,該HTTP response再次經過進入LB時,LB將寫入更新後的會話保持cookie。

這裏寫圖片描述

發佈了94 篇原創文章 · 獲贊 43 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章