一步一步理解 Java 企業級應用的可擴充套件性

OneAPM官方技術部落格發表於2016-01-13

摘要:本文主要介紹如何理解 Java 應用的擴充套件方式以及不同型別的擴充套件技術和具體技巧,介紹一些有關 Java 企業級應用的一般擴充套件策略。

老實說,“可擴充套件性”是個全面且詳盡的話題,而且往往得不到充分理解。人們通常認為可擴充套件性等同於高可用性,筆者見過程式設計新手和架構師“老手”都建議將叢集作為可擴充套件性和高可用性的解決方案。建議確實沒錯,但問題是,人們通常是通過網際網路搜尋,而非實際理解應用本身的情況來實現叢集。

筆者並未自稱“專家”,只想通過這篇文章介紹一些有關 Java 企業級應用的一般擴充套件策略。

問題

可擴充套件性並非 Java 企業級平臺規範內的標準元件。相關技術通常因供應商(應用伺服器)而異,並且往往需要使用不止一款產品(應用伺服器本身除外)。正因如此,設計可擴充套件的 Java 企業級應用才會有些棘手,要完成任務,往往不僅沒有可以參照的例項,而且要求我們必須徹徹底底地理解應用。

擴充套件型別

筆者確信你們不是第一次看到這些內容。擴充套件一般分為兩大類:縱向擴充套件,和橫向擴充套件。

擴充套件的第一個自然階段是縱向擴充套件。

  • 縱向擴充套件:包括給伺服器增加更多資源,例如記憶體 (RAM)、磁碟空間、處理器等。這在某些方案中具備實用價值,但經過特定時間點後就會發現,這種擴充套件費用高昂,不如藉助橫向擴充套件。
  • 橫向擴充套件:在這個過程中會增加更多機器或額外的伺服器例項/節點,這也叫做叢集(Clustering),因為所有伺服器是作為一個集體或叢集一起執行的。

高可用性不等於可擴充套件性

系統高度可用(擁有多個伺服器節點以方便故障轉移),並不表示系統可擴充套件。高可用性只是意味著,如果當前處理節點崩潰,請求會傳遞或轉移到叢集中的另一個節點,以便從開始處繼續。可擴充套件性則是通過增加可用資源(記憶體、處理器等)而提升系統特定效能(例如使用者數量、吞吐量、響應時間)的能力,即使將失敗請求傳遞到另一個節點,也無法保證應用會在這種場景中正確執行(原因我們會在下面揭曉)。

下面我們來了解一些關於可擴充套件性的觀點和相關討論。

讓橫向擴充套件的叢集達到負載均衡

假設您已經縱向擴充套件至最大容量,現在又用多個節點形成叢集,將系統進行了橫向擴充套件。接下來您要做的可能是在叢集基礎架構前放置一臺負載均衡器,讓負載分散在叢集各部分之間(如果要詳細瞭解負載均衡,大家可以參考其他方面的資料,在這裡我們重點還是說擴充套件問題)。

一步一步理解 Java 企業級應用的可擴充套件性

應用有狀態還是無狀態?

現在你已經橫向擴充套件了,這就夠了嗎?如果你的應用無狀態,即應用邏輯在處理請求時不依靠現有伺服器狀態,則橫向擴充套件已經足夠。

但如果應用具有 HTTP 會話物件、有狀態 EJB、會話域 bean (CDI、JSF) 等元件時,又會怎樣?這取決於具體客戶(具體來說,即呼叫執行緒),儲存特定狀態並依靠當前顯示的狀態來執行請求(例如,HTTP 會話物件可能會儲存使用者的身份驗證狀態、購物車資訊等)。

在橫向擴充套件或叢集式應用中,節點的任何叢集都可能為後續請求提供服務。如果首個請求的 JVM 例項處的狀態資料沒有被接收,其他節點會如何處理請求?

一步一步理解 Java 企業級應用的可擴充套件性

一步一步理解 Java 企業級應用的可擴充套件性

會話保持

會話保持配置可在負載均衡器層面上完成,確保來自特定客戶/終端使用者的請求始終被轉發到同一個例項/應用伺服器節點,即維持伺服器親和力。這樣,我們就緩解了所需狀態無法顯示的問題。但這裡有個陷阱 – 如果節點崩潰怎麼辦?狀態會被破壞,使用者會被轉至伺服器請求處理所依賴的、但不具備現有狀態的例項。

一步一步理解 Java 企業級應用的可擴充套件性

一步一步理解 Java 企業級應用的可擴充套件性

叢集複製

為解決上述問題,您可對應用伺服器叢集機制進行配置,以支援有狀態元件的複製,藉此可確保 HTTP 會話資料(和其他有狀態物件)顯示在所有伺服器例項上。如此一來,終端使用者請求便可轉至任何伺服器節點,即使某個伺服器例項崩潰或不可用,叢集中的其他任何節點都能夠處理請求。現在您的叢集就不是一般叢集了,而是複製叢集。

一步一步理解 Java 企業級應用的可擴充套件性

叢集複製特定於 Java 企業級容器/應用伺服器,最好查閱相關文件,瞭解如何複製叢集。一般而言,大多數應用服務都支援 Java 企業級元件(如有狀態和無狀態的 EJB、HTTP 會話、JMS 佇列等)叢集。

然而這造成了另一個問題 – 應用伺服器中的每一個節點都處理會話資料,導致 JVM 堆記憶體越來越多,因此垃圾回收也越來越頻繁,另外,複製叢集時還會消耗一定的處理能力。

有狀態元件的外部儲存

在另一層儲存會話資料和有狀態的物件,這可以藉助 RDBMS 實現,大多數應用伺服器本身就支援這一功能。

一步一步理解 Java 企業級應用的可擴充套件性

你可能已經注意到了,我們已經將儲存從記憶體層轉移到持久層 – 一天工作結束時,你可能會遇到由資料庫導致的擴充套件問題。不是說這一定會發生,但資料庫確實可能因為應用而過載,而後逐漸延時(例如在故障轉移時)。設想一下,從資料庫中再現整個使用者會話狀態以便用在另一個叢集例項中,不僅耗費大量時間,還會影響峰值負載下的終端使用者體驗。

最後的邊界:分散式記憶體中快取

這是最後的邊界,至少在我看來如此,因為它把我們帶回了記憶體方法。沒有比這更好的辦法了!Oracle Coherence、Hazelcast 這類產品或其他任何分散式快取/記憶體網格產品可用於清理有狀態的狀態儲存和複製/分佈 – 這就是快取層。好的一面是這些產品大多預設支援 HTTP 會話儲存。

一步一步理解 Java 企業級應用的可擴充套件性

這種結構設定意味著,應用伺服器的重啟不會影響現有使用者會話 – 給系統打補丁而不造成當機和終端使用者斷電(雖然並不像聽上去那麼容易,但顯然是個辦法!),這始終是好事。總的來說,其理念是:應用層和 web 會話快取層可獨立執行和擴充套件,彼此不受干擾。

分散式不等於重複式

這兩個詞之間存在巨大差異,就快取層而言,理解其中的差異是極為關鍵的。兩者各有長短:

  • 分散式:快取共享資料的各個部分,即資料集被分在各快取叢集節點之間(利用與產品特定的演算法)。
  • 重複式:所有快取節點都擁有所有資料,即每個快取伺服器都包含整個資料集的一份複本。

延伸閱讀(主要關於 Weblogic)

結束語

  • 高度可擴充套件性可能不是所有 Java 企業級應用的必要條件。但如果你打算構建網際網路/面向大眾的應用,將高可擴充套件性納入設計因素顯然非常實用。
  • 對於希望充分利用自動靈活性(經濟可行!)和高可用性等雲平臺(主要是PaaS)特點的應用而言,可擴充套件的設計是必要的。
  • 不難發現,有狀態的應用通常更難以擴充套件。完全「無狀態」或許無法實現,但我們應當朝這方面努力。

你用哪些技巧和方法來擴充套件 Java 企業級應用,快來和大家分享吧。

相關文章