1. 程式人生 > >分散式學習筆記三:分散式系統session一致性的問題

分散式學習筆記三:分散式系統session一致性的問題

session的概念

什麼是session?

伺服器為每個使用者建立一個會話,儲存使用者的相關資訊,以便多次請求能夠定位到同一個上下文。這樣,當用戶在應用程式的 Web 頁之間跳轉時,儲存在 Session 物件中的變數將不會丟失,而是在整個使用者會話中一直存在下去。當用戶請求來自應用程式的 Web 頁時,如果該使用者還沒有會話,則 Web 伺服器將自動建立一個 Session 物件。當會話過期或被放棄後,伺服器將終止該會話。

Web開發中,web-server可以自動為同一個瀏覽器的訪問使用者自動建立session,提供資料儲存功能。最常見的,會把使用者的登入資訊、使用者資訊儲存在session中,以保持登入狀態。

什麼是session一致性問題?

只要使用者不重啟瀏覽器,每次http短連線請求,理論上服務端都能定位到session,保持會話。

分散式session

單伺服器web應用中,session資訊只需存在該伺服器中,這是我們前幾年最常接觸的方式,但是近幾年隨著分散式系統的流行,單系統已經不能滿足日益增長的百萬級使用者的需求,叢集方式部署伺服器已在很多公司運用起來,當高併發量的請求到達服務端的時候通過負載均衡的方式分發到叢集中的某個伺服器,這樣就有可能導致同一個使用者的多次請求被分發到叢集的不同伺服器上,就會出現取不到session資料的情況,於是session的共享就成了一個問題。

如上圖,假設使用者包含登入資訊的session都記錄在第一臺web-server上,反向代理如果將請求路由到另一臺web-server上,可能就找不到相關資訊,而導致使用者需要重新登入。

Session一致性解決方案

1.session複製(同步)

 

思路:多個web-server之間相互同步session,這樣每個web-server之間都包含全部的session

優點:web-server支援的功能,應用程式不需要修改程式碼

不足

  • session的同步需要資料傳輸,佔內網頻寬,有時延

  • 所有web-server都包含所有session資料,資料量受記憶體限制,無法水平擴充套件

  • 有更多web-server時要歇菜

實現方式

① 設定tomcat ,server.xml 開啟tomcat叢集功能

這裡寫圖片描述

Address:填寫本機ip即可,設定埠號,預防埠衝突。

② 在應用裡增加資訊:通知應用當前處於叢集環境中,支援分散式 
在web.xml中新增選項

2.客戶端儲存法 

思路:服務端儲存所有使用者的session,記憶體佔用較大,可以將session儲存到瀏覽器cookie中,每個端只要儲存一個使用者的資料了

優點:服務端不需要儲存

缺點

  • 每次http請求都攜帶session,佔外網頻寬

  • 資料儲存在端上,並在網路傳輸,存在洩漏、篡改、竊取等安全隱患

  • session儲存的資料大小受cookie限制

“端儲存”的方案雖然不常用,但確實是一種思路。

3.反向代理hash一致性

 思路:web-server為了保證高可用,有多臺冗餘,反向代理層能不能做一些事情,讓同一個使用者的請求保證落在一臺web-server上呢?

 

 

方案一:四層代理hash

反向代理層使用使用者ip來做hash,以保證同一個ip的請求落在同一個web-server上

 

方案二:七層代理hash

反向代理使用http協議中的某些業務屬性來做hash,例如sid,city_id,user_id等,能夠更加靈活的實施hash策略,以保證同一個瀏覽器使用者的請求落在同一個web-server上

優點

  • 只需要改nginx配置,不需要修改應用程式碼

  • 負載均衡,只要hash屬性是均勻的,多臺web-server的負載是均衡的

  • 可以支援web-server水平擴充套件(session同步法是不行的,受記憶體限制)

不足

  • 如果web-server重啟,一部分session會丟失,產生業務影響,例如部分使用者重新登入

  • 如果web-server水平擴充套件,rehash後session重新分佈,也會有一部分使用者路由不到正確的session

session一般是有有效期的,所有不足中的兩點,可以認為等同於部分session失效,一般問題不大。

對於四層hash還是七層hash,個人推薦前者:讓專業的軟體做專業的事情,反向代理就負責轉發,儘量不要引入應用層業務屬性,除非不得不這麼做(例如,有時候多機房多活需要按照業務屬性路由到不同機房的web-server)。

實現方式:以Nginx為例,在upstream模組配置ip_hash屬性即可實現粘性Session。

upstream mycluster{
    #這裡新增的是上面啟動好的兩臺Tomcat伺服器
    ip_hash;#粘性Session
     server 192.168.22.229:8080 weight=1;
     server 192.168.22.230:8080 weight=1;
}

 

4.後端統一集中儲存

 

思路:將session儲存在web-server後端的儲存層,資料庫或者快取

優點

  • 沒有安全隱患

  • 可以水平擴充套件,資料庫/快取水平切分即可

  • web-server重啟或者擴容都不會有session丟失

不足:增加了一次網路呼叫,並且需要修改應用程式碼

實現方式:用開源的msm外掛解決tomcat之間的session共享:Memcached_Session_Manager(MSM)

a. 複製相關jar包到tomcat/lib 目錄下

JAVA memcached客戶端:spymemcached.jar

msm專案相關的jar包:

1. 核心包,memcached-session-manager-{version}.jar
2. Tomcat版本對應的jar包:memcached-session-manager-tc{tomcat-version}-{version}.jar

序列化工具包:可選kryo,javolution,xstream等,不設定時使用jdk預設序列化。
  •  

b. 配置Context.xml ,加入處理Session的Manager

粘性模式配置:

這裡寫圖片描述

非粘性配置:

這裡寫圖片描述

對於db儲存還是cache,個人推薦後者:session讀取的頻率會很高,資料庫壓力會比較大。如果有session高可用需求,cache可以做高可用,但大部分情況下session可以丟失,一般也不需要考慮高可用。

5.terracotta實現session複製

原理:Terracotta的基本原理是對於叢集間共享的資料,當在一個節點發生變化的時候,Terracotta只把變化的部分發送給Terracotta伺服器,然後由伺服器把它轉發給真正需要這個資料的節點。可以看成是對第3種方案的優化。

這裡寫圖片描述 
優點:這樣對網路的壓力就非常小,各個節點也不必浪費CPU時間和記憶體進行大量的序列化操作。把這種叢集間資料共享的機制應用在session同步上,既避免了對資料庫的依賴,又能達到負載均衡和災難恢復的效果。

 

總結

保證session一致性的架構設計常見方法:

  • session同步法:多臺web-server相互同步資料

  • 客戶端儲存法:一個使用者只儲存自己的資料

  • 反向代理hash一致性:四層hash和七層hash都可以做,保證一個使用者的請求落在一臺web-server上

  • 後端統一儲存:web-server重啟和擴容,session也不會丟失

 

對於方案3和方案4,個人建議推薦後者:

  • web層、service層無狀態是大規模分散式系統設計原則之一,session屬於狀態,不宜放在web層

  • 讓專業的軟體做專業的事情,web-server存session?還是讓cache去做這樣的事情吧。                                                            

Session和Cookie的區別和聯絡以及Session的實現原理

1、session儲存在伺服器,客戶端不知道其中的資訊;cookie儲存在客戶端,伺服器能夠知道其中的資訊。   
    
  2、session中儲存的是物件,cookie中儲存的是字串。   
    
  3、session不能區分路徑,同一個使用者在訪問一個網站期間,所有的session在任何一個地方都可以訪問到。而cookie中如果設定了路徑引數,那麼同一個網站中不同路徑下的cookie互相是訪問不到的。   
    
  4、session需要藉助cookie才能正常<nobr oncontextmenu="return false;" onmousemove="kwM(3);" id="key3" onmouseover="kwE(event,3, this);" style="COLOR: #6600ff; BORDER-BOTTOM: 0px dotted; BACKGROUND-COLOR: transparent; TEXT-DECORATION: underline" onclick="return kwC();" onmouseout="kwL(event, this);" target="_blank">工作</nobr>。如果客戶端完全禁止cookie,session將失效。

             http是無狀態的協議,客戶每次讀取web頁面時,伺服器都開啟新的會話,而且伺服器也不會自動維護客戶的上下文資訊,那麼要怎麼才能實現網上商店中的購物車呢,session就是一種儲存上下文資訊的機制,它是針對每一個使用者的,變數的值儲存在伺服器端,通過SessionID來區分不同的客戶,session是以cookie或URL重寫為基礎的,預設使用cookie來實現,系統會創造一個名為JSESSIONID的輸出cookie,我們叫做session cookie,以區別persistent cookies,也就是我們通常所說的cookie,注意session cookie是儲存於瀏覽器記憶體中的,並不是寫到硬碟上的,這也就是我們剛才看到的JSESSIONID,我們通常情是看不到JSESSIONID的,但是當我們把瀏覽器的cookie禁止後,web伺服器會採用URL重寫的方式傳遞Sessionid,我們就可以在位址列看到 sessionid=KWJHUG6JJM65HS2K6之類的字串。
             明白了原理,我們就可以很容易的分辨出persistent cookies和session cookie的區別了,網上那些關於兩者安全性的討論也就一目瞭然了,session cookie針對某一次會話而言,會話結束session cookie也就隨著消失了,而persistent cookie只是存在於客戶端硬碟上的一段文字(通常是加密的),而且可能會遭到cookie欺騙以及針對cookie的跨站指令碼攻擊,自然不如 session cookie安全了。
             通常session cookie是不能跨視窗使用的,當你新開了一個瀏覽器視窗進入相同頁面時,系統會賦予你一個新的sessionid,這樣我們資訊共享的目的就達不到了,此時我們可以先把sessionid儲存在persistent cookie中,然後在新視窗中讀出來,就可以得到上一個視窗SessionID了,這樣通過session cookie和persistent cookie的結合我們就實現了跨視窗的session tracking(會話跟蹤)。
            在一些web開發的書中,往往只是簡單的把Session和cookie作為兩種並列的http傳送資訊的方式,session cookies位於伺服器端,persistent cookie位於客戶端,可是session又是以cookie為基礎的,明白的兩者之間的聯絡和區別,我們就不難選擇合適的技術來開發web service了。

總之:

 

一、cookie機制和session機制的區別

  具體來說cookie機制採用的是在客戶端保持狀態的方案,而session機制採用的是在伺服器端保持狀態的方案。
  同時我們也看到,由於在伺服器端保持狀態的方案在客戶端也需要儲存一個標識,所以session機制可能需要藉助於cookie機制來達到儲存標識的目的,但實際上還有其他選擇。
二、會話cookie和持久cookie的區別
  如果不設定過期時間,則表示這個cookie生命週期為瀏覽器會話期間,只要關閉瀏覽器視窗,cookie就消失了。這種生命期為瀏覽會話期的cookie被稱為會話cookie。會話cookie一般不儲存在硬碟上而是儲存在記憶體裡。
  如果設定了過期時間,瀏覽器就會把cookie儲存到硬碟上,關閉後再次開啟瀏覽器,這些cookie依然有效直到超過設定的過期時間。
  儲存在硬碟上的cookie可以在不同的瀏覽器程序間共享,比如兩個IE視窗。而對於儲存在記憶體的cookie,不同的瀏覽器有不同的處理方式。
三、如何利用實現自動登入
  當用戶在某個網站註冊後,就會收到一個惟一使用者ID的cookie。客戶後來重新連線時,這個使用者ID會自動返回,伺服器對它進行檢查,確定它是否為註冊使用者且選擇了自動登入,從而使使用者無需給出明確的使用者名稱和密碼,就可以訪問伺服器上的資源。
四、如何根據使用者的愛好定製站點
  網站可以使用cookie記錄使用者的意願。對於簡單的設定,網站可以直接將頁面的設定儲存在cookie中完成定製。然而對於更復雜的定製,網站只需僅將一個惟一的識別符號傳送給使用者,由伺服器端的資料庫儲存每個識別符號對應的頁面設定。
五、cookie的傳送
1.建立Cookie物件
2.設定最大時效
3.將Cookie放入到HTTP響應報頭
  如果你建立了一個cookie,並將他傳送到瀏覽器,預設情況下它是一個會話級別的cookie:儲存在瀏覽器的記憶體中,使用者退出瀏覽器之後被刪除。如果你希望瀏覽器將該cookie儲存在磁碟上,則需要使用maxAge,並給出一個以秒為單位的時間。將最大時效設為0則是命令瀏覽器刪除該 cookie。
  傳送cookie需要使用HttpServletResponse的addCookie方法,將cookie插入到一個 Set-Cookie HTTP請求報頭中。由於這個方法並不修改任何之前指定的Set-Cookie報頭,而是建立新的報頭,因此我們將這個方法稱為是addCookie,而非setCookie。同樣要記住響應報頭必須在任何文件內容傳送到客戶端之前設定。
六、cookie的讀取
1.呼叫request.getCookie
  要獲取有瀏覽器傳送來的cookie,需要呼叫HttpServletRequest的getCookies方法,這個呼叫返回Cookie物件的陣列,對應由HTTP請求中Cookie報頭輸入的值。
2.對陣列進行迴圈,呼叫每個cookie的getName方法,直到找到感興趣的cookie為止
  cookie與你的主機(域)相關,而非你的servlet或JSP頁面。因而,儘管你的servlet可能只發送了單個cookie,你也可能會得到許多不相關的cookie。
例如:
  String cookieName = “userID”;
Cookie cookies[] = request.getCookies();
if (cookies!=null){
for(int i=0;i
 Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName())){
doSomethingWith(cookie.getValue());
}
}
}
七、如何使用cookie檢測初訪者
A.呼叫HttpServletRequest.getCookies()獲取Cookie陣列
B.在迴圈中檢索指定名字的cookie是否存在以及對應的值是否正確
C.如果是則退出迴圈並設定區別標識
D.根據區別標識判斷使用者是否為初訪者從而進行不同的操作
八、使用cookie檢測初訪者的常見錯誤
  不能僅僅因為cookie陣列中不存在在特定的資料項就認為使用者是個初訪者。如果cookie陣列為null,客戶可能是一個初訪者,也可能是由於使用者將cookie刪除或禁用造成的結果。
  但是,如果陣列非null,也不過是顯示客戶曾經到過你的網站或域,並不能說明他們曾經訪問過你的servlet。其它servlet、JSP頁面以及非Java Web應用都可以設定cookie,依據路徑的設定,其中的任何cookie都有可能返回給使用者的瀏覽器。
  正確的做法是判斷cookie陣列是否為空且是否存在指定的Cookie物件且值正確。
九、使用cookie屬性的注意問題
  屬性是從伺服器傳送到瀏覽器的報頭的一部分;但它們不屬於由瀏覽器返回給伺服器的報頭。 
  因此除了名稱和值之外,cookie屬性只適用於從伺服器輸出到客戶端的cookie;伺服器端來自於瀏覽器的cookie並沒有設定這些屬性。 
  因而不要期望通過request.getCookies得到的cookie中可以使用這個屬性。這意味著,你不能僅僅通過設定cookie的最大時效,發出它,在隨後的輸入陣列中查詢適當的cookie,讀取它的值,修改它並將它存回Cookie,從而實現不斷改變的cookie值。
十、如何使用cookie記錄各個使用者的訪問計數
1.獲取cookie陣列中專門用於統計使用者訪問次數的cookie的值
2.將值轉換成int型
3.將值加1並用原來的名稱重新建立一個Cookie物件
4.重新設定最大時效
5.將新的cookie輸出
十一、session在不同環境下的不同含義
  session,中文經常翻譯為會話,其本來的含義是指有始有終的一系列動作/訊息,比如打電話是從拿起電話撥號到結束通話電話這中間的一系列過程可以稱之為一個session。
  然而當session一詞與網路協議相關聯時,它又往往隱含了“面向連線”和/或“保持狀態”這樣兩個含義。
  session在Web開發環境下的語義又有了新的擴充套件,它的含義是指一類用來在客戶端與伺服器端之間保持狀態的解決方案。有時候Session也用來指這種解決方案的儲存結構。
十二、session的機制
  session機制是一種伺服器端的機制,伺服器使用一種類似於散列表的結構(也可能就是使用散列表)來儲存資訊。
  但程式需要為某個客戶端的請求建立一個session的時候,伺服器首先檢查這個客戶端的請求裡是否包含了一個session標識-稱為session id,如果已經包含一個session id則說明以前已經為此客戶建立過session,伺服器就按照session id把這個session檢索出來使用(如果檢索不到,可能會新建一個,這種情況可能出現在服務端已經刪除了該使用者對應的session物件,但使用者人為地在請求的URL後面附加上一個JSESSION的引數)。
  如果客戶請求不包含session id,則為此客戶建立一個session並且生成一個與此session相關聯的session id,這個session id將在本次響應中返回給客戶端儲存。
十三、儲存session id的幾種方式
A.儲存session id的方式可以採用cookie,這樣在互動過程中瀏覽器可以自動的按照規則把這個標識傳送給伺服器。
B.由於cookie可以被人為的禁止,必須有其它的機制以便在cookie被禁止時仍然能夠把session id傳遞迴伺服器,經常採用的一種技術叫做URL重寫,就是把session id附加在URL路徑的後面,附加的方式也有兩種,一種是作為URL路徑的附加資訊,另一種是作為查詢字串附加在URL後面。網路在整個互動過程中始終保持狀態,就必須在每個客戶端可能請求的路徑後面都包含這個session id。
C.另一種技術叫做表單隱藏欄位。就是伺服器會自動修改表單,新增一個隱藏欄位,以便在表單提交時能夠把session id傳遞迴伺服器。
十四、session什麼時候被建立
  一個常見的錯誤是以為session在有客戶端訪問時就被建立,然而事實是直到某server端程式(如Servlet)呼叫HttpServletRequest.getSession(true)這樣的語句時才會被建立。
十五、session何時被刪除
session在下列情況下被刪除:
A.程式呼叫HttpSession.invalidate()
B.距離上一次收到客戶端傳送的session id時間間隔超過了session的最大有效時間
C.伺服器程序被停止
  再次注意關閉瀏覽器只會使儲存在客戶端瀏覽器記憶體中的session cookie失效,不會使伺服器端的session物件失效。