序言
越來越多的關鍵應用運行在J2EE(Java 2, Enterprise Edition)中,這些諸如銀行系統和賬單處理系統需要高的可用性(High Availability, HA),同時像Google和Yahoo這種大系統需要大的伸縮性。高可用性和伸縮性在今天高速增長的互連接的世界的重要性已經證實了。eBay於1999年6月停機22小時的事故,中斷了約230萬的拍賣,使eBay的股票下降了9.2個百分點。
J2EE集羣是用來提供高可用性和伸縮性服務,同時支持容錯處理的一種流行的技術。但是,由於J2EE規範缺乏對集羣的支持,J2EE供應商實現集羣的方法也各異。這給J2EE架構師和開發人員帶來了很多困難。以下是幾個常見的問題:
l 爲什麼帶集羣功能的商業J2EE服務器產品如此昂貴?(10倍於不帶集羣功能的產品)
l 爲什麼基於單服務器環境構建的應用不能在集羣中運行?
l 爲什麼應用在集羣環境中運行得很慢,但在非集羣環境中卻快得多?
l 爲什麼集羣的應用移植到其他服務器中失敗?
理解這些限制和要素的最佳方法是學習他們的實現方式。
基本術語
在我們討論不同的集羣實現之前,先談談幾個概念。這有助於理解不同的J2EE集羣產品不同的設計結果和概念:
伸縮性(Scalability):
在一些大的系統中,預測最終用戶的數量和行爲是非常困難的,伸縮性是指系統適應不斷增長的用戶數的能力。提高這種併發會話能力的一種最直觀的方式就增加資源(CPU,內存,硬盤等),集羣是解決這個問題的另一種方式,它允許一組服務器組在一起,像單個服務器一樣分擔處理一個繁重的任務。
高可用性(High availability):
單一服務器的解決方案並不是一個健壯方式,因爲容易出現單點失效。像銀行、賬單處理這樣一些關鍵的應用程序是不能容忍哪怕是幾分鐘的死機。它們需要這樣一些服務在任何時間都可以訪問並在可預期的合理的時間週期內有響應。集羣方案通過在集羣中增加的冗餘的服務器,使得在其中一臺服務器失效後仍能提供服務,從而獲得高的可用性。
負載均衡(Load balancing):
負載均衡是集羣的一項關鍵技術,通過把請求分發給不同的服務器,從而獲得高可用性和較好的性能。一個負載均衡器可以是從一個簡單的Servlet或Plug-Ins(例如一個Linux box利用ipchains來實現),到昂貴的內置SSL加速器的硬件。除此之外,負載均衡器還需執行一些其他的重要任務,如“會話膠粘”讓一個用戶會話始終存在一個服務器上,“健康檢查”用於防止將請求分發到已失效的服務器上。有些負載均衡器也會參與我們下面將要談到“失效轉移”過程。
容錯(Fault tolerance):
高可用性意味着對數據正確性的要求不那麼高。在J2EE集羣中,當一個服務器實例失效後,服務仍然是有效的,這是因爲新的請求將被冗餘服務器處理。但是,當一個請求在一個正在失效的服務器中處理時,可能得到不正確的結果。不管有多少個錯誤,容錯的服務應當能確保有嚴格的正確的行爲。
失效轉移(Failover):
失效轉移是集羣中用來獲取容錯能力的另一項關鍵的技術。當一個結點失效後,通過選擇集羣中的另一個結點,處理將會繼續而不會終止。轉移到另一個結點可以被顯式的編碼,或是通過底層平臺自動地透明地路由到另一個服務器。
等冪方法(Idempotent methods):
等冪方法是指這樣一些方法:重複用相同的參數調用都能得到相同的結果。這些方法不會影響系統狀態,可以重複調用而不用擔心改變系統。例如:getUsername()就是等冪的,而deleteFile就不是。當我們討論HTTP Session失效轉移和EJB失效轉移時,它是一個重要的概念。
什麼是J2EE集羣
一個天真的問題,不是嗎?但我仍要用幾句話和圖來回答它。通常,J2EE集羣技術包括"負載均衡"和"失效轉移"。
圖 1 負載均衡
如圖1所示,負載均衡意味着有許多客戶端向目標對象同時發出請求。負載均衡器在調用者和被調用者之間,分發請求到與原始對象相同的冗餘對象中。伸縮性和高可用性就是這樣得到的。
圖 2 失效轉移
如圖2所示,失效轉移與負載均衡不同。有時客戶端會連續發請求到目標對象,如果請求中間目標對象失效了,失效轉移系統將檢測到這次失敗,並將請求重定向到另一個可用的對象。通過這種方式可以獲得容錯能力。
如果你想知道更多的有關J2EE集羣的知識,你就會問到一個基本的問題,“什麼對象可以集羣?”和“在我的J2EE代碼中哪裏會發生負載均衡和失效轉移呢?”。這些都是用來理解J2EE集羣的非常好的問題。實際上,並不是所有的對象都能被集羣的,並且負載均衡和失效轉移並不是在J2EE代碼所有地方都能發生。看看下面的例子代碼:
圖 3 例子代碼
在Class A的bussiness()方法中,instance1可以負載均衡嗎?或是當其失效,可以失效轉移到其他B的實例上嗎?我想是不行的!對負載均衡和失效轉移來說,必須要有個攔截器在調用者和被調用者之間分發或重定向請求到不同的對象上。Class A和Class B的實例是運行在一個JVM中緊密耦合的,在方法調用間加入分發邏輯非常困難。
什麼類型對象可以被集羣?——只有那些可以被部署到分佈式拓樸結構中的組件。
在我的J2EE代碼中,什麼地方會有負載均衡和失效轉移?——只在你調用分佈式組件的方法時。
圖 4 分佈式對象
在如圖4所示的分佈式環境中,調用者和被調用者被分離在有明顯邊界的不同的運行容器中,這個邊界可以是JVM,進程和機器。
當目標對象被客戶端調用時,目標對象的功能是在容器中運行的(這就是爲什麼我們說它是分佈式的原因)。客戶端和目標對象通過標準的網絡協議通信。這些特性就爲一些機制提供了機會可以介入到方法調用之間實現負載均衡和失效轉移。
如圖4,瀏覽器通過HTTP協議調用JSP對象,JSP運行在WEB服務器中,瀏覽器只需要返回結果而不關心它是怎麼運行的。在上述場景中,一些東西就可以在瀏覽器與WEB服務器之間實現負載均衡和失效轉移的功能。在J2EE平臺,分佈式技術包括:JSP(Servlet),JDBC,EJB,JNDI,JMS,WEB Service等。負載均衡和失效轉移就發生在這些分佈式方法被調用時。在後續部分我們將詳細討論這些技術。