大型Java分布式應用縱橫談
在當今應用架構里,分布式和應用與服務之間的通信都是核心思想。想要從分布式中獲益,你必須牢牢記住幾條基本的原則,否則你可能會很容易遇到性能和擴展性問題。在開發階段這些問題不會經常出現,但當你進行負載測試或產品化的時候,你可能會意識到你選擇的軟件架構不能滿足性能和擴展性需求。在這篇文章中,我們重點關注構建分布式應用需要記住的一些關鍵點。
分布式需要應用之間進行交互。范圍包括從大規模集群架構上簡單的點到點的交互,到動態的面向服務或基于服務的架構。跨系統邊界的通信也是提高軟件系統擴展性和可用性的關鍵。如今軟件架構已把分布式作為一個核心的必要的概念。Java平臺成為了核心的角色,因為它的分布式、有很好的API和產品支持這些特點。應用場景從像SAP這樣在標準軟件上的系統集成,到內部或外部的服務集成。SOA提供這樣的方法,使服務和應用變的靈活和可重用,可以對新的市場需求很快的做響應。另外,像使用網格計算,虛擬機和多核刀片機的趨勢,導致越來越多的集群應用的出現。這主要是由于追求高可擴展性和高可用性驅動的。而且云計算的發展趨勢表明,分布式平臺將來會更加流行。另外,系統正變得希望能更動態的增加其靈活性。例如,在運行時添加應用節點。這些趨勢也導致了系統結構變得越來越復雜。對于開發人員來說,則更難理解產品中服務調用是如何實現的了。這種復雜性和缺乏對相應知識的了解,很容易導致資源消耗的增加(CPU,內存,網絡)和性能的降低。
面具后的惡魔
如今,遠程技術使分布式應用的實現更加簡單。底層通信的細節和服務端和客戶端的基礎結構對開發人員是透明的?,F在,如果要把一個Java類暴露為一個服務,有時只需要簡單的加一個注解到這個類上。服務也可以被工具生成的代理很容易的訪問。如下圖所示,但是,這僅僅是冰山的一角。
圖1.遠程協議的上層架構
遠程堆棧的核心塊是對象的序列化和傳輸的格式化。通常,應用的開發者不需要知道這些。但是,這也是很多性能問題產生的原因。效率不高的序列化意味著,通過網絡傳輸了很多不需要的數據。復雜對象的顯示和大量的數據,在序列化和反序列化期間,導致CPU和內存的使用會很高。底層的基礎架構和它的配置對應用的性能有很大的影響。在客戶端,主要是連接的管理和底層線程模型。在分布式應用中使用連接的指導方針和數據庫的連接很像。建立一個連接需要一定的時間。但這同樣要看是什么協議。例如,建立一個HTTPS的連接的開銷要大于一個簡單的TCP/IP連接。同時,連接又是系統很重要的資源。所以,使用連接池很重要。正確的配置在這里也很關鍵,因為錯誤的配置文件給我們帶來的壞處要多于好處。線程的模型涉及到請求如何被處理。重要的是請求是被同步還是異步處理。同步通信阻塞一個進程直到收到相應。在異步通信中,當收到響應時會調用一個回調。這就允許這個線程被其他事務使用。在服務端,可用的工作線程數量就是定義的并行處理的最大服務請求數。網絡本身也是分布式應用的一個重要組件。網絡是比影響性能更加限制其可擴展性的重要的瓶頸資源。這塊通常在開發時會被忽視,因為沒有調用實際的網絡。
遠程調用之美在于...
這有很多可以選擇,Java提供了非常多的可能性和技術來實現分布式應用。遠程技術的選擇對應用的架構、性能和擴展性有十分重要的影響。最“老的”的但是幾乎是用的最廣的遠程協議是RMI(如下圖)。
圖2.RMI架構
RMI是J2EE應用的一個標準協議。像它的名稱暗示的一樣,設計時就是為了調用遠程Java虛擬主機上的對象提供的方法。對象在服務端被暴露出來,這時客戶端就可以通過代理調用這個對象。同樣的服務端對象被多個線程使用。線程池被RMI基礎設施管理。通信通過TCP/IP被處理,并且使用JRMP或針對RMI的基于IIOP GIOP(CORBA協議)的協議。應用服務端也提供自己的屬性協議來優化其性能。如服務端的引用需要管理一樣,RMI基礎設施也提供了垃圾回收器來管理引用。這個分布式垃圾回收器(DGC)本身也使用RMI協議來管理服務器端的對象生命周期。除了客戶端和服務端很強大,RMI還有一些其他的實現。關于RMI的詳細介紹及應用請參考51CTO之前的文章《用RMI實現基于Java的分布式計算》。
RMI只支持同步通信,缺點上面已經討論過了。另外,不能為數據驅動的服務提供低級緩存,因為它是基于2進制協議的。開發人員和系統架構能夠改變基礎設施的配置參數來優化性能。JMS是J2EE平臺上使用的第二多的協議。如下圖:
圖3.JMS架構圖
有別于RMI, JMS是一個異步的協議。通信是基于隊列的,以便監聽器可以對消息作出反應。JMS不是一種標準的遠程調用協議,但是它仍然能夠滿足服務與服務之間的交互。在SOA中非常重要的很多ESB的實現,就采用基于JMS的中間件來進行服務之間的信息傳遞。由于JMS是異步的,一些典型的同步問題就可以避免。在很多系統中,高可擴展性的關鍵在于能夠很快的釋放資源(像線程)。在很多情況下,異步處理是唯一合適的方法。JMS提供很多不同的傳輸格式。XML是最通用的消息格式,但二進制格式也是可能的。消息結構的設計是應用架構的一個重要部分,因為它可以直接影響到應用的性能和可擴展性。
基于SOAP的WEB Service(如下圖)和其他相關的WS-*也在Java 企業應用領域中變得越來越重要。
圖4.同步和異步SOAP架構
設計SOAP是為了替換CORBA,而且一開始就得到了業界的強烈支持。因為WS-I之間的共同努力,不同平臺差不多能夠很容易的連接起來。SOAP是一種基于XML的RPC協議,所以很容易和浪費帶寬聯系到一起。#p#
越來越多的基于REST的服務開始取代SOAP。Java中的REST服務在JSR 311中有說明,是基于HTTP所支持的基本操作而設計的。但是,REST不是作為RCP協議,而是面向資源的,為了訪問和操作(web)資源而設計的。這兩個協議都支持同步通信。這也是底層HTTP協議所要求的。WS-地址對SOAP協議進行擴展,所以它也允許異步服務的實現。REST最大的優點是,能夠很容易的通過HTTP代理實現緩存。REST依靠使用HTTP底層協議,無論如何都和用的機制。
可能犯的錯
分布式應用的很多地方都可能出現潛在的問題,如圖所示:
圖5 分布式應用的問題起因
在客戶端,主要的問題在于糟糕的交互設計-太多的服務調用,或者選擇了錯誤的通信模式。同步事務運行時間過長很容易導致性能問題。在通信層,大量的數據和過多的服務調用所產生的高的網絡負載是主要問題。在服務端,不適當的服務接口設計和不合適的序列化策略的使用導致性能和擴展性問題。我們下面仔細看下這些問題。
分布式應用的問題起因
通信協議的正確選擇主要取決于系統的整體架構和底層的需求。如果你工作在有mainframe、Java和.NET組件相互交互的特異環境中,用SOAP是行不通的。在純Java環境中,在JRMP上使用RMI仍是性能最優,可擴展性最好的解決方案,你能夠獲得開箱即用的編程支持。在很多SOA實現中,SOA和基于Web Service的實現同義而語。所以有越來越多的使用SOAP作為RPC協議的純Java應用案例出現,盡管采用這樣的方法一點有點都沒有。調查顯示,和RMI-JRMP相比,經常使用SOAP還是有意義的。除了描述過的標準協議,一些其他的基于XML的和二進制協議也在一些應用中使用。Hessian的性能就不錯。另外,還有一些其他編程語言的實現。例如使用Spring把POJOs暴露給遠程調用使不改變實現就在不同的協議間切換變得相對容易。Spring 支持RMI, HTTP, Hessian, Burlap, JAX-RPC, JAX-WS 和 JMS。
反模式:饒舌的應用
在搭建分布式應用時,一個核心的原則就是盡量減少遠程調用。這些意味著數據序列化的開銷,建立連接的開銷和附件的網絡負載。另外,CPU,內存和網絡資源的消耗限制了可擴展性。所以,為遠程應用的接口設計一種方法,來確保必要的服務交互數最小是十分重要的。尤其是那些起初是在本地搭建的,然后為了可擴展性原因遭遇了大量服務交互的應用。這些問題大多會在負載測試或產品化時出現,但當本地開發測試時一點問題都沒有。可以采用適當的性能管理方法,在開發過程中分析遠程行為就可以避免這些問題。下圖顯示的是一個通過dynaTrace分析一個應用的遠程行為的例子 。
基于這個分析,接口能夠重新創建,應用邏輯能夠重新設計來減少遠程調用的次數??赡艿姆椒ㄊ?,合并幾個方法的邏輯為一個,或在幾個調用請求周邊的對象處,使用數據容器。特定數據的位置也可以幫助減少遠程調用,因為在需要的地方數據是可用的。尤其當讀數據時,使用cache可以很大程度上提高性能和可擴展性。在軟件設計的早期,服務的分發和可能的通信在成為需求或將成為需求時已經考慮到是很重要的。
反模式:大格式消息
當調用遠程的服務時,這通常意味著數據會在不同的協議上傳輸。像XML在SOAP協議上傳輸或二進制數據在RMI協議上傳輸。大多數技術傳輸對象的數據或對象本身。大多數情況下,序列化的發生是在遠程實現的底層。序列化的開銷和所傳輸對象的大小相對應。在實際情況下,我們進行序列化的開銷要占到98%。怎么會這樣?一個鑒權服務接口需要一個用戶對象來授權。這個用戶對象不僅有用戶名和密碼,還有很多屬性,關聯到其他用例的數據引用。標準的SOAP序列化要創建幾千字節的數據消息。這些數據要被服務解析并映射到用戶對象結構上,導致大量CPU和內存的消耗。解決方案再明顯不過了。接口要重構,只需要用戶ID和密碼。所以,除了選擇正確的遠程技術,消息內容的設計對構建好的性能和可擴展性的應用很重要。通常正好符合設計的很好一般對象會帶來高性能的回報。
反模式:分布部署
分布式的Java企業級應用會導致一個應用分割成多個服務和一些部署單元部署到一些應用服務上。分布式的有一個組件新的部署包不需要重新部署到其他組件上。另一個可能性是,大量使用的服務能夠部署到獨立的硬件或被部署多次。有大量部署單元的復雜應用,服務的交互變得越來越難理解。這會導致2個交互頻繁的服務被部署到不同的硬件上從而產生很多的遠程調用情況出現。在大規模應用中,分析交互的頻率和數據大小與部署結構一致是很重要的。很多時候,從分布式部署到本地可以使性能得到很大的提升而不需要損失靈活性和可擴展性。尤其對那些無狀態的服務,把它們部署到不同的節點來提升其本地性。
結論
從這些反模式中可以看出,在應用的設計初期階段就考慮可擴展性是很重要的。它是應用架構的一個關鍵驅動。在后期提高性能和可擴張性在多數或大多數情況下工作會越困難。對應用產品的詳細分析來識別遠程調用的頻率或大體積數據,優化系統的一致性是不可或缺的。如果你遇到了相似的或不同的問題,請讓我知道,我好擴充我的反模式記錄。
【更多Java分布式技術】
原文標題:Performance Considerations in Distributed Applications
鏈接地址:http://blog.dynatrace.com/2009/09/28/performance-considerations-in-distributed-applications/