層級: 中級
Geoff Hambrick (ghambric@us.ibm.com), 傑出工程師, IBM
2006 年 2 月 13 日
本文探究了服務導向架構中各種形式的松耦合,並重點介紹什麼時候是使用訊息驅動 Bean 所提供的非同步處理能力的最佳時機。
摘自 IBM WebSphere 開發人員技術期刊。
在每個專欄中,EJB 倡導者都採用獨特的前後銜接的對話方式與實際客戶和開發人員進行交流,並在期間針對某一大家關注的設計問題推薦解決方案。其中並沒有介紹任何確定性細節,也沒有提出“新穎的”或專有的體繫結構。要瞭解更多資訊,請參見 EJB 倡導者簡介。
您對松耦合的定義是否太過狹窄呢?
由 於這是 2005 年的最後一篇文章,而且這次交流的是 Java Platform、Enterprise Edition (Java EE) 組件而非會話和實體 Bean,所以可以通過這篇文章來很好地總結本專欄所進行的一年之久的討論,並將所有的組件一起放到完整的服務導向架構中。
問題:太過於關注 SOA 中的會話和實體
親愛的 EJB 倡導者:
到 目前為止您的專欄全部都是關於服務的會話和實體 EJB,它對於直接連接 Java 應用程式是很重要的,例如基於 網頁用戶端的 HttpServlets 或富(我們不喜歡說“胖”)用戶端的 Swing 應用程式。但我們曾聽說服務導向架構都是與松耦合有關的。這不就意味著要儘可能地使用 SOAP 來提供使用戶端和伺服器應用程式能夠獨立啟動並執行語言中立性和非同步協議?換句話說,為什麼您對 JMS 和訊息驅動 Bean 討論得不多呢?
署名:
Feeling Disconnected
對松耦合有許多方面要考慮
親愛的 Disconnected:
EJB 倡導者關注的是應用程式的服務層,而對用戶端介紹得很少,因為我對古諺語形式追隨功能 (form follows function) 深信不疑。
其原因在於,許多進行 SOA 的項目之所以失敗,是因為它們沒有首先建立定義服務的良好模型就開始著手實現細節。
這種傾向相當正常,因為我在 SOA 項目中接觸到的大多數人都是架構師和編程人員,他們知道棘手的始終是在細節上,想要儘快解決它們。所以,一旦我們認同好服務的特徵是粗粒度、無狀態、可通過中介傳遞和適應的(請參閱 Is it ever best to use EJB components without facades in service oriented architectures?),很明顯帶有資料轉送對象的會話 Bean 就會在實現中扮演重要角色。
但上期專欄介紹了使用實體 Bean 及其 Home 方法代替會話 Bean 的可能性,從而使人們對會話 Bean 是否真的是必需的產生了疑問。圖 1 顯示了同時使用的兩種方法。
圖 1. 用有待使用的會話和實體 EJB 實現的服務
圖 1 顯示了當使用傳遞過去的會話 Bean 時,純實體方法(自 EJB 2 開始可用)如何才能有更少的組件和更短的路徑長度。其中使用綠色框表示用戶端,藍色框表示各種介面和 Facade 類。橙色框是最常訪問的實體 Bean。框和框之間使用雙向箭頭串連,箭頭標有在組件之間通訊所使用的協議;藍色箭頭表示相同 JVM 中的 Java 調用,紅色箭頭表示遠端連線(在本例中使用 RMI/IIOP)。為了表示端到端的流向,對流程箭頭進行編號,其中 A1-A10 表示從 Java 用戶端經過會話 Bean 到實體和返回的流程,B1-B4 表示從用戶端直接到實體 Bean 和返回的流程。
用戶端用於檢索服 務介面的編程模型也很簡單,雖然在圖中沒有表示出來。檢索會話 Bean 介面需要在 JNDI 上下文中尋找會話 Home 並用它建立一個會話;實體 Home 僅僅需要一次尋找,其方法可以直接調用而不需要建立對 EJB Object 的引用。以下兩段程式碼範例顯示了它們之間的區別。
清單 1. 定位和調用遠端工作階段 EJB 方法
Context initCtx = new InitialContext(); Object obj = initCtx.lookup("java:comp/env/ejb/OrderEntry"); OrderEntryHome home = (OrderEntryHome)PortableObjectRemote( obj, OrderEntryHome.class ); OrderEntry ref = home.create(); // Method must be invoked on a session reference CustomerData data = ref.getOpenOrderForCustomer(cID);
|
清單 2. 定位和調用等效遠程實體 EJB Home 方法
Context initCtx = new InitialContext(); Object obj = initCtx.lookup("java:comp/env/Customer"); CustomerHome ref = (CustomerHome)PortableObjectRemote( obj, CustomerHome.class ); // Note how the method is invoked directly CustomerData data = ref.getOpenOrder(cID);
|
圖 1 和相關的程式碼範例顯示了 Java EE 編程模型的真正好處,不管您是否選擇使用實體 Home 方法。編程模型逐漸改進並提供向後相容。簡而言之,您的最佳實務能夠得以發展而不必強制更改現有的應用程式。另外,JNDI 上下文提供了一個不容忽視的松耦合相關方面:實現獨立性。
會話或實體 EJB 組件的遠程介面提供了位置獨立性。 使用遠程介面使得在相同的 JVM 中部署用戶端應用程式和服務元件成為可能,其中它對系統(例如使用 HttpServlets 的 Web 應用程式)的回應時間、輸送量和可維護性目標有意義。圖 2 顯示的正是這樣的配置,其中企業品質應用伺服器(如 IBM WebSphere Application Server)在聯合部署了用戶端和服務元件時“短路”遠程介面以使用 Java 並按引用傳遞(不管服務實現為實體 Home 方法還是會話 Bean)。流程 A1-A6 顯示了與服務元件聯合部署的 HttpServlet 的使用。流程 B1-B4 顯示了它如何被遠程富用戶端 Java EE 應用程式重用。
圖 2. 部署到本地 Web 應用程式和遠程富用戶端的服務
但這聽起來像是您已確定松耦合的最重要方面是語言中立性和非同步作業。並且您認為需要非同步作業會使得必須在伺服器端使用訊息驅動 Bean (MDB) 和 JMS。
在 實現 MDB 時,許多編程人員使用的方法是通過其遠程介面調用表示服務的 EJB 組件,以使上述位置獨立性最大化。然後,不管服務實現是遠程還是本地部署,MDB 的目的都是作為簡單的適配器,將 MQ 層串連到承載服務的 EJB 容器。JMS 可能由非同步用戶端應用程式(如果它是 Java)或 MDB 使用(通常用於應答隊列)。圖 3 顯示了服務實現的另一個輸入通道的這一配置。
圖 3. 用提供非同步用戶端通道的 MDB 部署的服務
流程 C1-C2 與 D1-D6 分別顯示,以闡釋用戶端和伺服器流程的獨立性。C2 和 D6 只是對 Writer 是否寫入訊息進行“確認”,並不意味著等待。清單 3 顯示了 MDB 的典型結構,該結構應該有助於闡明它必須做的處理:
清單 3. 典型的訊息驅動 Bean 實現
public class OrderSubmitMsgHandler implements MessageDrivenBean { private transient MessageDrivenContext context = null; // Interface supported by either the session Bean or entity Home private transient OrderEntry ref; public void ejbCreate() { // Cache the home according to either snippet one or two } public void ejbRemove() {} public void setMessageDrivenContext(MessageDrivenContext mdc) { context = mdc; } // Message acknowledge and database update participate public void onMessage(Message inMessage) { // Parse the customer from the message try { ref.submit(customer); } catch (CustomerDoesNotExist e) { // Send JMS message to reply queue from exception } catch (OrderNotOpen e) { // Send JMS message to reply queue from exception } catch (OrderHasNoItemsException e) { // Send JMS message to reply queue from exception } } }
|
EJB 倡導者現在將回到您提到過的語言中立性問題。看起來您已經將語言中立性的要求與非同步處理的要求緊密聯絡起來了。沒有理由不能將這些考慮分開;解析 SOAP 訊息和用它調用會話 Bean 的能力應該與該訊息的處理是非同步(通過 MQ 或者傳遞 JMS 等效訊息的另一個協議)還是同步(例如通過 HTTP,甚至 IIOP)無關。事實上,Java EE 應用程式上的一些早期“發明”的 Web 服務使用 HttpServlet 來解析通過 HTTP 傳遞的 XML 訊息。這種方法最終發展為 SOAP/HTTP。圖 4 顯示可以在 EJB 組件所實現的服務之上提供的另一個路徑。
圖 4. 將語言中立性和非同步性的考慮分開
Web Service Servlet 和 Message Driven Bean 可以共用對來自從訊息串或 HttpServletRequest 提取的流的資料轉送對象進行解析的代碼。類似地,響應或應答可以共用從資料轉送對象(它可以是 Exception 的一個執行個體)產生流的代碼。
希望這有助於您理解 Java EE 組件的布置,所有這些都將提供某種對服務導向架構很基本的松耦合形式。
對話到此結束,
您的 EJB 倡導者
仍然有太多選項要選擇
親愛的 EJB 倡導者:
謝謝。
我 以前不認為像 JNDI 這樣的服務和遠程介面會提供松耦合。我還可以瞭解如何將 SOAP 和 MQ 的概念“緊耦合”(按照您提到的方法),以及應該如何儘可能將它們分開。所以將解析和產生 SOAP 訊息看作由 Web 服務 Servlet 和 MDB 重用的服務本身是很有意義的。
但謝謝,我不要這樣。
在這次討論之前,SOA 看似非常簡單:每個服務都公開一個 SOAP/MQ 介面。現在看起來有好多選擇要考慮,並且既然將 SOAP 訊息的解析和產生看作服務,那麼為什麼不想建立一個獨立的會話 Bean 來封裝它們以便中所顯示的那樣進行重用呢?
再次打擾您,我還是:
Still Disconnected
並非一切都是服務:要使用業務模型加以判定
親愛的 Disconnected:
這次討論是當過快要著手實現細節時會出現的情況的一個很好的例子。EJB 倡導者專欄 Which type of EJB component should assemble the data returned by a service? 非常重要,因為它描述了一種定義服務的自頂向下方法,這種方法將服務直接與商務程序模型相關聯。這裡總結這種方法的基本細節:
開發一個商務程序模型,顯示重要域對象的生命週期中的主要裡程碑:
我們使用狀態轉換關係圖來描述這一模型,其中狀態代表裡程碑,轉換代表促使變成該狀態的事件。轉換可以看作應用程式所提供的服務(有關樣本請參見圖 5)。
我們使用 UML "Actor" 符號來擴充狀態轉換關係圖,以顯示當處於該狀態時對象的所有者。狀態的所有者負責發起轉換,從而驅動應用程式的安全模型(也請參見圖 5)。
對於商務程序中的每個狀態,我們也對每個需要支援轉換動作的域對象的屬性和它們之間的關係進行建模。我們通常使用的符號是 UML 類別關係圖(有關樣本,請參見圖 6)。為每個狀態建立獨立的關係圖可以讓我們隨時對變化的對象“形狀”進行建模。這些關係圖驅動持久性資料。
開發一個使用者介面流程模型,顯示在典型“會話”期間來自商務程序的給定參與者如何與系統相互動。
與商務程序模型一樣,我們也使用狀態轉換關係圖,其中狀態代表螢幕和對話方塊,而轉換代表實際的 UI 事件,如選擇功能表項目和按下按鈕。根據商務程序轉換指定與轉換相關的動作。
還是和商務程序模型一樣,我們構建一個類別關係圖來顯示必須在每個狀態中可見的資料以支援各種選擇。這意味著該資料必須可以從使用者輸入派生,並以自頂向下的方式驅動服務上的讀取操作。
與商務程序模型不同,我們不需要用 Actor 符號擴充狀態關係圖,因為關係圖本身可以視為單個使用者角色的“生命中的一天 (day in the life)”。
圖 5. 顯示訂單生命週期的狀態轉換關係圖樣本
圖 6. 顯示 open 訂單“形狀”的類別關係圖樣本
這種綜合方法可以確保:
將正確的操作分組到一個服務(與商務程序生命週期中的一個狀態相關聯的所有轉換和 read 方法)。
每個服務的用途很好理解(根據相關業務對象來指定動作)。
調用服務需要的前置條件和會產生的後置條件是相通的(目前狀態和可選的監護、以及下一狀態都是通過轉換指定的)。
標識了負責調用服務的使用者角色(參與者與每個狀態相關聯)。
不管是否實現,方法簽名都不會提供此資訊,但此資訊對於好的 SOA 來說是很關鍵的。否則,編程人員將陷入另一種傾向:一旦有懷疑,就再次構建。因為 SOA 的開發成本比較高(要為圖 4 所示的完全松耦合提供各種中介器和適配器),所以這種反對重用的傾向可能會導致獲得好處最少。
對您關於簡單性的回覆中提出的問題的回答是,此資訊一點都沒有強制您以某種方式公開介面,EJB 倡導者知道簡單只存在於旁觀者眼中。如果您想要使服務開發人員不必進行選擇,只需為每個服務提供圖 5 中所示的所有“藍色”組件即可:
遠程服務介面,用於提供同步 Java EE 用戶端訪問服務作業的位置獨立性。
與每個操作相關聯的 MDB,通過遵循 JMS 的 MQ 實現來提供非同步非 Java 用戶端訪問。您可以選擇編寫此 MDB 或不同的 MDB 以期待 JMS 用戶端的 Java 訊息(從而避免 HTTP 解析開銷)。
與每個操作相關聯的 Web 服務 Servlet,用於提供同步 SOAP over HTTP 用戶端訪問。
因為有人會擔心這種方法將產生大量未使用的組件,所以另一種簡單的方法是,應用 EJB 倡導者喜歡的內容來調用面向用戶端的體繫結構 (COA);以對用戶端最自然的方式給它們提供使用服務正好需要的內容。
這種 COA 方法需要考慮商務程序和 UI 模型的細節,以便為每種方法挑選最可能的候選者。例如:
業 務流程生命週期中的狀態之間的轉換很可能是非同步服務的候選者,因為它們將是相關域對象的“所有者”變體。例如,在 open 訂單應用程式(我們在上述樣本中調用此 OrderEntry)中,submit 方法可以簡單地將訂單的狀態更改為“submitted”,並發送一條 JMS 訊息來將其複製到已提交訂單應用程式中(我們將這稱為一次 OrderFulfillment)。
狀態間的轉換通常應該是同步的,因為所有者沒有 改變。舉個例子來說明為什麼這些操作不應該是非同步,設想一下,如果您進入一位書商的 Web 應用程式,還必須輪詢或等待發布-訂閱事件來顯示目錄或將物品添加到購物車中!對於想要通過非同步通道使用偽同步方式的讀者,請查閱 Bobby Woolf 關於設計訊息傳遞系統的書籍。
只有對於沒有相關的非 Java 用戶端和服務的整合情境,才提供 SOAP over HTTP 或 MQ。
使用 COA 方法,可以“準時 (just-in-time)”開發組件,這就是 EJB 倡導者喜歡推薦它的原因。
對 您的問題(為什麼不將所有的東西都看作是服務,甚至是與中介器和適配器相關聯的轉換)的回答的最後一點是:簡單的回答超出了“過猶不及”這一事實。在開發 SOA 或 COA Java EE 應用程式時,最好將服務視為商務程序模型上的操作。服務與應用程式的功能需求有關。中介和相關聯的轉換與非功能需求有關,例如可靠性、可用性、有效性、可 維護性和可移植性。如果您將中介或相關聯的轉換視作第一個類服務,則最終將使應用程式的真實用途變得模糊。
我知道這其中有很多方面的內容需要理解,所以當您應用這些方法時,遇到相關細節問題不要嫌麻煩,儘管與我聯絡。
對話到此結束,
您的 EJB 倡導者。
結束語
這些交流中談論了 Java EE 如何為採用服務導向架構的應用程式提供完整的實現架構,其中每個組件或 API 都在一些“松耦合”方面扮演重要角色:
作業系統獨立性是由 Java 本身提供的,因為 Java 為組件提供“一次編寫,到處運行”的語言,使得您的代碼與基礎作業系統相分離。
實現獨立性是由 Java Naming 和 Directory Interface (JNDI) 提供的,它具備在運行時將邏輯名稱綁定到實現的能力。
位置獨立性是由遠程介面使用 RMI over IIOP 提供給無狀態會話 Bean 或封裝服務的實體 Home 方法的。RMI/IIOP 是一個相當快的有狀態串連,但擴充性不那麼好。
Web 服務器獨立性是由 HttpServlets 提供的,它可以響應同步 HTTP 協議。與 RMI/IIOP 不同的是,HTTP(通常)是一個擴充性很好的無狀態協議,但由於訊息大小的限制,而且每次都需要在用戶端和伺服器之間建立和取消串連,所以不能執行得很好。
應用獨立性是由非同步 Java Messaging Service(或非 Java 用戶端的 MQ)和訊息驅動 Bean 提供的(用於隊列處理常式)。
語言獨立性是由標準訊息格式(如 SOAP)提供的,不管它是在 RMI/IIOP、HTTP 還是 MQ 上傳遞。因此,SOAP 可以被每個組件使用,但只有在需要時才使用它,這一點很重要。
相信您可以找到其他方法,我們樂意看到這樣的結果。困難的是需要處理與每種方法相關聯的權衡。這應該能夠讓您忙到明年。
參考資料
- 您可以參閱本文在 developerWorks 全球網站上的 英文原文。
- Java Platform, Enterprise Edition (JEE) specification (formerly J2EE);這裡將“2”去掉是為了將用於企業的 Java 平台的主要概念與具體的版本分開。
- Enterprise Java Programming with IBM WebSphere, Second Edition,Kyle Brown、Gary Craig、Greg Hester、Russell Stinehour、W. David Pitt、Mark Weitzel、JimAmsden、Peter M. Jakab 和 Daniel Berg 著。前言由 Martin Fowler 撰寫。
- Enterprise Integration Patterns:Designing, Building, and Deploying Messaging Solutions,Gregor Hohe 和 Bobby Wolfe 著。包含大量與訊息傳遞應用程式相關聯的很好的模式和反模式。
關於作者
|
|
|
Geoff Hambrick 是 IBM 軟體服務部 WebSphere Enablement Team 的首席顧問,居住在德克薩斯州的 Round Rock(在奧斯汀附近)。Enablement Team 通常通過深入的技術講座及短期概念驗證為售前流程提供支援。Geoff 於 2004 年 3 月 被選為 IBM 的傑出工程師,他的工作是建立並傳播在 IBM WebSphere Application Server 上開發 J2EE 應用程式的最佳實務。 |