J2EE集羣不太瞭解的人首先能夠看看附件裏面的《解開J2EE集羣的神祕面紗》, 講的挺好的。javascript
J2EE的服務器集羣主要的就是負載均衡 和失敗轉移 這些。java
負載均衡這個話題都爛大街了,隨處能夠找到相關的帖子或博文,我也就不談了。編程
可是這些帖子中大部分都只談了負載均衡,頂多再說一下 Tomcat 的 HttpSession 複製(失敗轉移的一種解決方案吧)。更有甚者,直接決定「集羣中服務器節點宕機丟失的部分 HttpSession 不礙事」。。。緩存
個人感受就是,tomcat
像 Tomcat 的 HttpSession 交叉複製,若是集羣中服務器過多對性能的影響確定很是之大。服務器
像 JBoss、WebLogic 等的服務器鏈式 HttpSession 複製,若是一個服務器節點宕機,此節點的下一個服務器節點就得負責兩個服務器的用戶請求。是否是有點怕人奧。session
至於「集羣中某個節點宕機就宕機, HttpSession 丟失無所謂」這樣的觀點也是挺匪夷所思的,畢竟後臺中2000個 HttpSession 同時丟失的後果不是那麼容易承擔的。併發
爲了解決上面這些問題,我本身琢磨了一套方案,感受挺不錯的(不知道網上是否是有相似「輪子」,反正我是沒有搜索到)。 就發出來共享一下,嘿嘿app
我想到的是緩存服務器和Web服務器雙向備份HttpSession ,這個。。。。咱語文挺爛的,表達的很差。負載均衡
雙向備份就是集羣中多個Web服務器分散保存 HttpSession ,同時在緩存服務器中也分散保存這些 HttpSession (每一個 HttpSession 正常狀況下只須要複製一次),至於怎麼分散各位且聽我慢慢說來。
我使用了三個緩存服務器(Memcached),三個Web服務器(Tomcat)進行思路實現和測試。
先說說思路吧: 三個緩存服務器和三個Web服務器以下分佈
Tomcat_A | Tomcat_B | Tomcat_C |
Memcached_A | Memcached_B | Memcached_C |
Tomcat_A 的 HttpSession 分佈備份在Memcached_B 和 Memcached_C 中
Tomcat_B 的 HttpSession 分佈備份在Memcached_A 和 Memcached_C 中
Tomcat_C 的 HttpSession 分佈備份在Memcached_A 和 Memcached_B 中
個人構思就是這樣: 在服務器正常運行狀況下,使用 Stick-Session 方式,同一個 HttpSession 由負載均衡器判斷 HttpSession 歸屬交給同一個服務器執行,這樣一來,正常狀況下,服務器節點的運行就是徹底獨立的、就好像是單臺服務器運行同樣。 HttpSession 複製備份也是隱式的,經過哈希碼將這個 HttpSession 隱式備份在相應哈希碼分佈的 Memcached 上。
假設運行中 Tomcat_A 忽然宕機了(我咔嚓關了Tomcat_A,嘿嘿),那麼負載均衡器就會沒法將請求分發給 Request 相對應的Tomcat_A ,負載均衡器便會把請求分發給其餘的Tomcat_B 或 Tomcat_C ,這兩臺服務器會接收到原屬於綁定在 Tomcat_A上的請求 ,他們沒有相應的 HttpSession 啊, 而後他們就主動從 Memcached 集羣中尋找這個 HttpSession 。若是找到的話,他們便主動把這個 HttpSession 「收錄」爲本身的, 即將 JSESSIONID 修改成本身的。這樣一來,原屬於 Tomcat_A 的 HttpSession 會隨機分佈給其餘沒有宕機的服務器節點。
這是服務器節點宕機的狀況、
若是緩存器節點宕機了怎麼辦?
假設 Memcached_A 宕機,那麼整個集羣中保存在 Memcached_A 中的Session備份就丟失,那麼 Tomcat_B 和 Tomcat_C中的Session就沒有了備份。 若是這時這兩臺服務器再掛斷的話,Session就真的丟失了(用戶感情傷不起啊)。爲了保證 「整個集羣中隨時都存有同一個Session的備份 」, Tomcat_B 和 Tomcat_C 應該爲自身的Session負責。他們應該主動把本身的 Session 再備份一次(能夠採用臨時新建線程的方式, 畢竟宕機不是頻繁的麼。屬於特殊狀況),再次備份到其餘的可用 Memcached 節點中。
這樣,整個程序中能夠徹底無視 單臺機器的忽然宕機, 一分鐘內整個集羣應該能把宕機服務器的 Session 再備份一次吧!
關於這個想法,網上有一個開源項目叫作 memcached-session-manager 。 我試過它,感受不怎麼好用。它只能說實現了部分個人想法吧,沒有徹底實現。測試中,仍然存在 HttpSession 丟失的狀況。而且它有着使人髮指的效率問題!
我測試了一下, 普通的 JSP 頁面處理只耗費 62.3ms (機子挫)。而添加上這個插件以後,普通的 JSP 頁面處理須要消耗2.18s !!!這真是使人髮指的效率!
靠人不如靠本身、我便本身實現了本身的思路。
使用的緩存程序就是 Memcached 2.6, 我添加在附件裏了。
我使用的 Memcached 客戶端是 XMemcached (國產)、支持國產麼,而且它的效率據某公司測試證實,比 SynMemcached 略高一點點。
下載地址:XMemcached
我就是經過替換了 Tomcat 原有的 StandardManager(MemcachedManager替換)、StandardSession(MemcachedSession替換) 來實現的, 而後加入了隱式的 HttpSession 備份操做。若是須要的話,還會從緩存程序中嘗試獲取 HttpSession。
我還經過在 MemcachedManager 中向 Context 中加入閥門(Valve)在每次請求以後再根據 MemcachedSession 的屬性是否被修改來判斷是否更新緩存(事務一致性麼,整個請求處理過程是一個原子)。 不涉及setAttrubute、removeAttribute的操做是不會觸發緩存更新的,而且 setAttribute 若是設置重複值也是不會引發緩存更新的。
在 XMemcached 中我添加了DisConnect 事件的監聽器、 若是某條鏈接斷開(Memcached服務器節點宕機)的話,會觸發Web服務器將 Session 再次更新入其餘未斷開的Memcached服務器節點中。
整個過程實現其實挺簡單的、 jar 包我也放在附件中。
最後就說說我實現的這個「無名」插件的效率、 測試中我挺費解的, 添加了這個插件的 Tomcat 竟然比正常的 Tomcat 處理JSP頁面還快。使用它以後,處理一個正常一樣JSP頁面只須要 了 57.5ms ,嘿嘿,快了幾毫秒(應該是測試數據偏差)。
使用這個「無名」插件挺簡單的、 就是修改 context.xml ,添加一個:
在測試中,我胡亂關閉Web服務器和Memcached服務器, 每次請求的 HttpSession 都沒有碰見丟失的狀況,嘿嘿,感受挺不錯的。性能上也是毫無影響。 可是這是沒有經受過任何生產考研的!
有想法的人能夠試試,源碼就在jar包中。若是有什麼優化想法咱歡迎交流!
感受俺寫的不錯的、頂一個啊。點個精華、良好神馬的安慰一下俺嘛
========================================================================
關於這個 failover 解決方案的設計思想:
我是感受不必爲了 HttpSession failover再另外添置一個 Memcached 服務器集羣。 在現有的 Tomcat 集羣上的每一個節點上都綁定一個 Memcached 節點這樣構成一個附加的 Memcached 服務器集羣是否是更好? 既不花錢、對性能基本上沒什麼影響(Memcached 效率很高的,有人測試了上億數據量也僅僅佔用了10%的CPU)。
關於 attribute 可能被修改而沒有觸發緩存更新的問題:
這個問題就是這樣 session.getAttribute("key") = newObject; 這樣的代碼, 我沒有深看因此還不太清楚此 getAttribute 方法是獲取"引用"仍是"複製"。
若是是「複製」的話就好說了,若是是引用的話。。。嘿嘿,我目前是沒有好辦法處理了,只有靠開發人員本身注意編程規範了(通常設置session屬性的話都是調用setAttribute吧?)。 但願有哪位朋友想到了解決辦法更俺交流一下。