@鄭昀匯總
與分布式緩存在高並發和高可用下所要解決問題差不多。
一.圖示:
二.高並發下分布式Session需解決的問題:
- 透明處理存儲介質的故障轉移
- 動態增刪節點,減小“緩存顛簸”問題
- 保證數據在各個節點的分布均衡
- Session 序列化和反序列化
三.保證“基本可用 Basically Available”的分布式Session方案:
Eric A. Brewer 在 1988 年提出的 BASE 策略,即
Basically Available、
Soft state、和
Eventually consistent。
互聯網大多數應用更強調可用性,即犧牲高一致性,獲得可用性或可靠性。
基本可用 Basically Available 的定義:在分布式系統部分損壞的時候,允許部分內容不可用,但是其他部分仍舊可用。因此稱這種系統為“基本可用”。比如,一個數據存儲系統由 五個節點構成。其中一個發生了損壞,這時只有20%的數據不能訪問,其他80%數據仍然可用。那么就可以稱這種系統為基本可用的。
基於 memcache 的
Hash取模算法(hash() mod n,hash() 取用戶ID,n為節點數) 實現的分布式 Session 方案,就屬於基本可用:
第一,如果節點發生故障,該節點上的所有用戶 Session 丟失,系統無法自恢復。第二,如果系統壓力突然增大,需要臨時增加機器節點。按照 Hash取模的算法,在增加機器節點的這一時刻,大量緩存無法命中(其實還都存在之前的節點上),導致大范圍的緩存穿透,壓力會直接打到數據庫上。第三,根據 LRU 緩存失效算法,memcache 里存儲的 key/value 有可能被踢出,用戶 Session 容易丟失。
針對 Hash取模 的
改進辦法是:
四.基於一致性哈希算法的 memcache 解決方案
1)一致性哈希幫我們解決的是,當機器節點減少時,緩存數據能進行最少重建。
2)還能解決 Session 數據的分布均衡問題。
3)當機器節點宕機,這部分數據必然丟失。由於節點數目變化,有可能對部分沒有丟失的數據也要重建。
但上面的方案都解決不了“
一個節點失敗后,它所存儲的 Session 如何由其他節點獲取以便接替失效節點,實現集群的容錯(Failover)”。
鄭昀先介紹下面幾個概念:
五.Sticky Session、Non-sticky Session和Replicated Sessions
- Sticky Sessions:粘性會話。即同一個會話中的請求必須被轉發到同一個節點上,除非該節點宕機才轉發到故障轉移節點。一個節點宕機,所存儲的 Sessions 完全丟失。通俗的話就是,將用戶“粘”在某一個服務器節點上。
- Non-Sticky Sessions:非粘性會話。每一次請求都可能轉發到不同節點。
- Replicated Sessions:把一個節點上的 Sessions 復制到集群的其他節點上,防止數據丟失,允許失效無縫轉移。如node 0復制到node 5,node 1復制到node 6,以此類推。多數應用服務器(如 Tomcat )都支持會話復制機制。
當用戶數量和集群數量達到一定規模后,Session 復制就可能成為性能瓶頸。於是人們提出了
從第三方緩存恢復失效節點數據 的方案,開源產品
Memcached-Session-Manager(下面簡稱MSM)就是基於這個思想。
六.MSM的工作原理
MSM 支持 Tomcat 6 和 7,即它主要解決的是 Tomcat 的高可用性。
它的特性為:
- 支持 sticky sessions 和 non-ticky sessions 模式。
- 沒有單點故障。
- 能處理 tomcat 故障轉移
- 能處理 memcache 故障轉移
- pluggable session serialization
- 允許異步存儲 session,提高響應速度
- sessions 只有真正被修改時,才會發給 memcache
6.1.Sticky Session 模式下的工作原理
即,
Tomcat 的本地 session 為主 session,memcache 中的 session 為備 session。
第一步,所有 Tomcat 節點都需要安裝 MSM;每一個 Tomcat 會有自己的本地 session。第二步,當一個請求執行完畢之后,如果對應的 session 在本地不存在(即這是某一個用戶的第一次請求),則將該 session 復制一份至 memcache 。第三步,當該 session 的下一個請求到達時,會使用 Tomcat 的本地 session。請求處理結束之后,session 的變化會同步更新到 memcache,保證數據一致。第四步,如果當前 Tomcat 節點失效,下一個請求會被路由給其他 Tomcat。這個 Tomcat 發現請求所對應的 session 並不存在,於是它將查詢 memcache,如果查詢到了,則恢復到本地 session。
這樣就完成了容錯處理。
6.2.Non-sticky Session 模式下的工作原理
即,
Tomcat 的本地 session 為中轉 session,memcache 1 為主 session,memcache 2為備 session。
第一步,收到請求,加載備 session 到本地容器;備 session 加載失敗,則從主 session 加載;第二步,請求處理結束之后,session 的變化會同步更新到 memcache 1和 memcache 2,並清除Tomcat 的本地 session 。
session data 要想存入 memcache,必須能序列化和反序列化。
七.基於 kryo 的序列化方案
所有序列化策略都必須提供下面的特性:
- 序列化:能處理循環引用。
- 序列化/反序列化:支持對一個共享對象(Shared Object)的引用。
- 反序列化:支持 private classes 。
- 反序列化:支持沒有默認構造函數的類。
下面是 MSM
Wiki 所列出的表格:
Serialization Strategy Value for transcoderFactoryClass attribute |
Requires java.io.Serializable |
Cyclic Dependencies |
Shared objects |
Private classes | Classes without default constructor |
Different class versions | Copy Collections before serialization |
Custom Converter |
Comment |
java serialization(default, bundled with msm) de.javakaffee.web.msm.JavaSerializationTranscoderFactory |
Yes | Yes | Yes | Yes | Yes | No (Though, if the serialVersionUID is set to 1L, classes can be deserialized even if the new class version has new fields) |
No | No | |
msm-kryo-serializer de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory |
No | Yes | Yes | Yes (for Sun JVMs) | Yes (for Sun JVMs) | No (not yet) | Yes | Yes (Converter must extend KryoCustomization, SerializerFactory or UnregisteredClassHandler) | Reflection based, Kryo is used for binary serialization/deserialization |
msm-javolution-serializer de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory |
No | Yes | Yes | Yes (for Sun JVMs) | Yes (for Sun JVMs) | Yes (During deserialization, fields that are not existing in a class are ignored) | Yes | Yes (Converter must extend [apidocs/javolution/xml/CustomXMLFormat.html CustomXMLFormat]) | Reflection based, Javolutionis used for actual xml encoding/decoding, it also does the object reference handling |
MSM 作者的觀點是:
- Java serialization 是一個魯棒性非常好、也被廣泛證明了的技術,但 IMHO(恕我直言),它最大問題就是無法處理類的版本:向下兼容時新版本如何反序列化老版本序列化的數據流,如還要向上兼容的話老版本如何反序列化新版本序列化的數據流。為了確認兼容性,測試量將是版本數的平方。
- Kryo 是一個非常快的二進制序列化庫。在 thrift 與 protobuf 的性能 benchmark 中,kryo 也是最快的序列化工具庫之一。他推薦使用 Kryo ,就是因為超凡的性能。
八.基於 ZooKeeper 集群的分布式 Session 方案
要解決基於 memcache 方案的數據丟失問題,可以引入持久化存儲介質
ZooKeeper(下面簡稱 ZK)。
依托於 ZK 的一致性復制(在多個副本間保證數據的強一致性)和容錯能力,結合上面的 MSM 思想,
由 ZK 負責 session 數據的存儲,而我們自己實現的 session manager 將負責 session 生命周期的管理。
九.微軟系的解決方法
ASP.NET 有自己的分布式 Session 解決方案,Session State Server ,即 web.config 里指定 sessionState 的 mode 為“StateServer”即可。
鄭昀既可以在 web.config 里指定一個 State Server :

也可以實現 System.Web.IPartitionResolver 的接口,自行決定如何構造 Session State Server 連接字符串,從而支持一組 State Servers。
鄭昀也可以用 sessionState 的 partitionResolverType 屬性設定即可:

微軟的這個解決方案缺點是,Session State 中的序列化和反序列化對象將成為主要性能消耗之一,最好使用基本類型來存儲所有的 Session State 數據。
參考資源:
2) jacktan,2011,
基於ZooKeeper的分布式Session實現
3) Maarten Balliauw,2008,
ASP.NET Session State Partitioning
4)timyang,2009,
某分布式應用實踐一致性哈希的一些問題
7)developerWorks,2010,
關於 Java 對象序列化您不知道的 5 件事