SOA系列文章(二):服务设计原理:服务模式和反模式
服務設計系列的法則已經發展到最佳通信實踐和取樣相關編碼的程度。本文提供了設計和實現網絡服務的基本原理,并且對面向服務的體系結構(SOA)的相關概念做了一個簡要的回顧,以及有關于幾種模式和反模式的詳細討論,當構建網絡服務時,開發者可以利用它們。它可應用于能進行網絡服務開發和配置的任何編程語言或平臺。
- 關于SOA設計原理系列
- 面向服務的體系結構(SOA)簡介
- 模式與反模式
- 總結
關于SOA設計原理系列
這是一系列研究設計更有效的網絡服務為目標的論文集的第一步。這第一篇論文是在微軟的模式與實踐小組的協助下完成的,以后的論文也可能會包含與其他組或個人的協作成果,不僅限于微軟內部小組或個人。
讀者應該記住關于樣本代碼的警告,這些代碼只是為了SOA設計原理系列開發的,它們僅用作舉例說明目的。建議讀者學習這些樣本代碼,而不應該試圖將這其中的任何代碼用于實際的開發環境。如果讀者決定將樣本代碼用到實際開發環境中,微軟公司不承擔任何可能由此引發的損失。
樣本代碼的系統配置要求:
- Microsoft Window XP(在其他平臺上沒有試過)
- Microsoft .Net Framework 1.1
- Microsoft Internet Information Server 6.0
- Microsoft SQL Server or Microsoft Access
- Microsoft Visual Studio 2003 不是必需的,但它會提供一個更好的全面的過程。
如果發生不支持樣本代碼的情況,本文的作者期待你的反饋。
注意:SOA不斷地迅速發展,在這系列中的論文和樣本代碼也許會被修正,使其更加成熟。
面向服務的體系結構(SOA)簡介
SOA已經變成一個眾所周知的縮寫詞,如果你問其他兩人關于SOA的定義,你極有可能會得到兩個完全不同,甚至可能相互矛盾的答案。一些人將SOA描述成了一個業務上的IT行業基礎或底層結構,其他人則希望SOA能提高IT行業的效率。在許多方面,SOA有點象Godfrey Saxe寫的盲人和大象的故事,每個人都各有不同地描述大象,因為他們中的每個人都被他們的個人經歷所左右(比如,摸大象鼻子的人相信大象是一條蛇,而摸大象長牙的人則認為大象是一桿矛),Saxe的大象太容易描述了,因為它就是一個存在的實體。然而,SOA是很難去描述的,因為設計理論無法作為一個實體顯示出來。
SOA是一種創建那些從自治服務中構建出的系統的途徑,它使得整合變得可預見性而不是在事后進行計劃,最終的解決方法很可能就是組合由不同語言開發的各種服務,這些服務以多種安全模式和業務流程運行在不同的平臺。這個概念聽起來是難以置信的復雜,但它已經不是新的了,一些人也許會認為,SOA是從那些基于已有技術的分布式系統的設計和開發中發展而來,有許多與SOA相關的概念,比如服務、發現以及后來的綁定,也與CORBA和DCOM密切相關。同樣地,一些服務設計原理也很大程度上與早期的基于封裝、抽象和接口的OOA/OOD技術有共同點。
SOA也提示了一個很明顯的問題——嚴格地來講,服務是什么?簡單來說,一個服務是一段程序,能通過定義完整的信息交換機制來進行互動,服務必須設計得可用而且穩定,當服務配置等為了改變而重新構建時,服務也要隨之構建并持續下去。靈活性經常被視為SOA帶來的最大的利益之一,舉個例子,一個機構的業務流程如果在一個松耦合的底層結構上運行,那么它肯定比那些束縛于潛在的單一的應用軟件的機構組織更開放更能適應變化,因為后者需要幾周時間去改變以適應最小的變化。松耦合的系統導致了松耦合的業務流程,因為業務流程不再受制于潛在底層結構的限制。服務以及相關的接口必須保持穩定,而且使它們可以被重新設置、整合以滿足業務上的不停的變化。服務通過標準的接口和定義明確的消息來維持穩定,換句話說,SOAP和XML schemas用作消息的定義。如果服務被設計成僅僅知道信息如何傳遞或得到,而且只執行簡單的粒度較小的函數,那么它極有可能被一個更大SOA底層結構所重用。正如早前所提到的,回想OO設計原理中的封裝和接口設計會有助于我們設計和構建重用的網絡服務。通過進一步理解和掌握面向服務的四個原則,我們能將OO原理擴展到世界上所有的網絡服務。
原則1:明確清楚的分界線
服務通過在明確定義的分界點上的消息傳遞而相互作用,跨區域層次的服務可能因為地理位置、信任因素或者其他執行要素而花費很大的代價。一個分界點就是在一個服務的公共接口和它內部的執行之間的邊界,一個服務的分界點通過WSDL來發布的,而且也許包含了已有服務的期望聲明。跨區域的服務因為下面幾點原因而被認為是一項昂貴的任務:
- 目標服務的物理位置是個不可知因素;
- 安全以及信任模式一般在跨越每個分界點時會發生變化;
- 在一個服務的公共和私有部分之間,數據的編組和廣播需要對于其他資源的信任,對于服務本身來說,其中的某些資源是外部不可見的。
- 當服務配置被構建以適應改變時,服務也被構建以持續下去。這意味著,由于網絡的重配置或者遷往其他物理位置時,一個原本可靠的服務而突然發生改變甚至退化。
- 用戶一般都不知道內部流程實現的保密程度。一個用戶只能有限地控制所指定服務的運行。
面向服務集成模式告訴我們,廣域服務受限于網絡的潛伏因素,網絡故障和分布式系統故障,但是一個本地服務不會出現這樣的問題。大量的錯誤檢測和改正方案必須寫出以處理使用遠程對象接口帶來的問題。我們假定跨區域是花費很高的,但也必須處理一些本地方法的配置中出現的警告,這些本地方法的作用就是使得跨區域的可能減到最小。一個只有單一本地方法和對象的系統也許會獲得更高的性能,但是無法重用已經定義的服務(這種技術可比喻成OOP中的拷貝和粘貼,它在服務的版本上也遇到一樣的問題)。
關于SO的第一個原則,還有幾點應該記住的:
- 清楚你的邊界點。服務有一個協約來定義它所提供的公共接口,服務的所有交互都通過公共接口發生,這個接口包括了公有流程和公有數據形參,公共流程是進入服務的入口,而公有數據形參則代表了流程使用的信息。如果我們使用WSDL來表示一個簡單的協約,那么<message>表示公共數據,而<portType>表示公共數據,而 則表示公有流程。《Data on the Outside vs. Data on the Inside》這篇文章更加詳細地調研了這些論點。
- 服務應該易用。當設計一個服務時,開發者必須保證其他開發者很容易地使用它。服務的接口(協約)也應該被設計得不用破壞本身便可進一步擴展。(這個主題將在此系列后面的論文中更詳細地講解。)
- 避免RPC接口。在類似RPC的模型中,外部信息傳遞應該是最主要的,它隔絕了用戶與服務的內部實現,使服務開發者去進一步發展他們的服務,盡量減少服務用戶之間的沖突(通過使用公有消息而不是公有方法來進行封裝)。
- 保持服務接口區域小。一個服務提供的公共接口越多,這個服務也就越難被使用和維持,所以,應該提供很小的定義明確的接口在你的服務中,這些接口應該相對簡單,只是接收定義明確的輸入消息以及返回定義明確的輸出消息,一旦這些接口被設計完成,它們應該是靜態的,作為服務的內部實現的對外接口,這些接口滿足服務所必須支持的恒定的設計需求。
- 內部實現細節不應該泄漏到服務分界點外,否則極有可能導致服務與用戶之間更緊密的耦合。服務的內部實現應該屏蔽,因為它包括了版本和服務更新的選擇。這篇文章的反模式部分列舉了此類問題的詳細例子。
原則2:服務是自治的
服務是實體,它們獨立地配置、更新和管理。開發者不應該對于服務邊界之間的空間做出假設,因為這些空間會比服務邊界本身變得還快。比如,服務邊界應該是靜態的,將減小版本的更新給用戶帶來的影響。服務的邊界一般都是穩定的,而關于策略、地理位置或網絡技術等服務配置選項則會經常地變化。
服務可以通過URI動態地設定地址,這使得地理位置、配置技術的改變或者不斷地發展變化對服務本身只有很小的影響(這就好比服務的通道)。這些變化對服務也許只能造成很小影響,但它們能會對基于這些服務的應用造成破壞性的作用。假如你當前正在使用的一個服務明天將被遷移到新西蘭的一個網絡中時,該怎么辦?這種響應時間的變化也許會對用戶造成不可預期或不可意料的影響。對于服務如何被使用,設計者們應該持一種非樂觀的態度,比如服務失敗、服務相關的行為(服務級)要面對變化等。異常處理和補償措施的適當引入必須與任何一個服務點相關。除此之外,用戶也需要去改變以適應所使用服務的最小響應時間,比如,用戶需要有關于安全、性能、事務處理以及其他因素的不同級別的服務。一個靈活的策略可以使一個單一的服務支持SLAs(其他的策略也許關注于版本、定位或其他方面)。在不同級別服務上的通信性能期望都是保守估計,因為一個服務沒必要知道其他服務的內部實現。
不僅僅是用戶需要以非樂觀的態度來面對服務的運行,而且服務提供商也是如此。有時不用服務本身來通報,用戶應該對服務失敗這種情形有所預料。服務提供商也不能保證用戶按照正常過程使用服務,比如,用戶可能試圖使用惡意的信息去通信或者違反正常服務交互的規則,服務的內部也必須試圖去糾正這些不適當的使用,不管用戶是什么意圖。
服務被設計成自治的,但也沒有服務是孤立的。一個基于SOA的解決方案還未成型,它其中包括了許多為一個具體方案而配置的服務。可以想到的是,在面向服務的環境中還沒有可以用作指導的權威著作——一個管弦樂隊指揮者的觀念是不完善的(也就進一步意味著“回滾”的概念也是有缺陷的——這也是在后面論文中的議題之一)。實現自治服務的關鍵在于隔絕和退耦,所設計的服務相互獨立地進行配置,并且只能用協議約定的信息和規范來進行通信。
正如其他的服務設計的原則方法,我們能從過去OO設計的經驗中學到很多。Peter Herzum和Oliver Sims在業務構件制造上(Business Component Factories)的工作提供了一些有趣的關于自治構件性質的見識。當他們的大部分工作與基于構件的解決方案相吻合時,基本的設計原則也仍然適用于服務設計。
考慮到這些因素,這里有一些簡單的設計要求來保證與SOA的第二原則相吻合:在配置和使用這些服務的系統中,服務應該被相互獨立地配置和更新。協約應該根據“一旦被發布即不能更改”的假定來設計,這使得開發者不得不在他們的計劃設計中考慮靈活性。考慮服務所面臨的最壞情況,并加以解決。從用戶的角度來講,在服務的可用性和性能方面應該所有考慮或計劃,從服務提供商的角度,則應該考慮到對服務的(故意)不適當使用,以及有可能出現的服務失敗等情況。
原則3:服務共享模式和協約,不是類
如先前提到的,服務的交互應該建立在服務的策略、模型和基于協約的行為上。服務的協約一般都由WSDL來定義,服務集合的協約能用BPEL來定義說明(也就是說,BPEL使用WSDL來定義集合中每個服務的協約)。
在一個問題領域內,大部分開發者都定義類來代表各種實體(比如,顧客、訂單、產品),類把數據(消息)和對數據的操作結合到一個單獨的編程語言或具體平臺構造中。服務則打破了這種模型結構以求最大的靈活性和互動性,使用基于XML schema的消息來進行服務通信的方式對于編程語言和平臺是不明確的,它只能確保服務邊界之間的互動性,Schema定義了消息的結構和內容,服務的協約則定義服務本身的行為模式。
總之,一個服務的協約包含了下列元素:
- XML Schema定義的消息相互交換格式
- WSDL定義的消息交換模式
- WS-Policy定義的功能需求
- BPEL也能被用作業務流程級別的協約以集合多個服務
用戶將依靠服務的協約去調用服務并與之交互,考慮到這方面的可靠性,服務的協約必須能保持穩定。服務協約利用XML Schema(xsd : any)的擴展性質和SOAP過程模型(可選擇報頭),應該盡可能清楚地設計出來。
第三原則的最大挑戰就是它的性能。一旦服務的協約被公布,那么它將很難修改,因為要減小對現有用戶的影響。位于內部與外部數據之間的臨界線對于服務是否能成功配置和重用是十分關鍵的。公共數據(傳遞于服務之間的數據)應該基于統一的標準,以確保跨服務的訪問,而私有數據(服務內的數據)被封裝在服務內。在很多方面,服務像是一個運行電子商務組織的較小個體。就像一個組織必須將外部的訂單轉換成內部訂單格式那樣,一個服務也必須將協約規范的數據轉換成其內部格式。我們在面向對象的數據封裝方面的知識說明了一個相似的概念——服務的內部數據只能通過服務的協約來操作。Pat Helland在Data on the Outside vs. Data on the Inside””中闡述了幾個關于公共和私有數據的論題。
考慮到這些因素,這里有一些簡單的設計要求來保證與SOA的第三原則相吻合:
- 確保服務協約的穩定,以減小對用戶的影響。在某種意義來講,協約意味著公共數據表示(數據)、消息交換模式(WSDL)和可配置的性能和服務級別(策略)
- 為了盡可能減小誤解,協約必須定義清楚明確。除此之外,它也能通過XML 語法和SOAP過程模型的擴展性,來兼容以后新版本的服務。
- 避免在公共和私有數據表示之間的模凌兩可的狀態。服務的內部數據格式應該對用戶隱藏,而它的公共數據模式是不變的(更適宜基于一個統一的實際行業標準)。
- 當對于服務協約的更改不可避免時,要及時更新服務。這樣能減小現有用戶執行所帶來的破壞。
原則4:基于策略的服務兼容
這個原則經常被考慮得最少,而它又或許是關于實現靈活網絡服務的最重要因素之一。只通過WSDL來通信服務互動的一些需求是不可能的,策略表達式可以從語義兼容(消息如何被傳遞或者傳遞給誰)中分離出結構兼容(什么被通信)。
操作要求能在機器可讀的策略表達式中清楚闡述,策略表達式包含了一套能共同操作的語義來管理服務的行為。WS_Policy規范定義了機器可讀策略框架,它能表達服務級別的策略、并在執行時間找到它們執行,比如,一個國家安全服務也許需要一個策略去強化某個具體服務級別(舉個例子,已滿足指定標準的護照照片必須再被恐怖分子識別系統再次檢查)。與服務相關的策略信息能被很多其他實施不同檢查點的服務所重用,網絡服務策略不需要增加一行其他的代碼就能加強這些需求,這一節也說明了策略框架如何提供其他的有關于服務需求方面的信息,也列舉了用于服務定義和執行的一些已公布的編程模型。
一個策略的斷言定義了一種行為,這種行為就是一個策略的必要條件。(在上面一節中這個斷言就是恐怖分子識別系統的檢查)。斷言中有具體領域的語義,并最終在相互無影響的應用于各種行業的具體領域的詳細說明中定義(建立WS-Policy框架概念)。
策略驅動的服務依然在不停地發展,開發者也應該保證他們的策略斷言在服務期望和服務語義兼容上盡可能的清晰。
模式與反模式
既然你已經對SOA概念有了初步的了解(包括SO設計原則),那現在就開始應用我們所學到的,這篇論文的下面部分介紹了兩種反模式和三種模式,每種反模式和模式都是基于先前討論的概念來設計的。
為什么有模式和反模式?
人們趨向于在模式中思考和交流。Christopher Alexander已經寫了幾本關于模式語言的書,他定義模式是從一個具體中來的一個抽象,并且在具體的上下文中保持重現。模式和模式語言用來描述實踐、設計證明和獲取供別人學習的經驗,模式是一條途徑去迅速理解應用這些模式的設計指導方針和各種上下文,而對于反模式,顧名思義,與模式相反。在模式提供指導和實踐時的地方,反模式則說明了公共設計缺陷,并且作為一種從其他錯誤中學習的方法,這篇論文的剩下部分介紹的所有模式與反模式能指導我們開發更有效的網絡服務。
這篇論文中的反模式與模式都將使用下列格式:
- 上下文:模式與反模式的簡要背景描述。有了上下文,讀者才能去應用一個模式,以及在具體證明反模式之前就能識別一個反模式的特征。
- 問題:一個簡單的聲明。被用來構成與模式或反模式相關的對象。
- 影響:在應用已有模式和識別反模式時,一些額外的其他的因素也必須考慮進來。
- 解決方法:已有反模式或者應用相關模式的必需步驟的詳細描述。
- 征兆和結果:反模式中,這些因素能生成反模式;而在模式中,征兆和結果也許提到了其他因素和考慮點來應用相關模式。
反模式中有如何證明相關設計缺陷的附加建議。
代碼樣本
- 每個樣本代碼都被打包成安裝文件(MSI)并且有README文件來說明如何安裝和配置樣本代碼。
- 讀者應該知道關于樣本代碼使用的警告。樣本代碼只用作說明目的,讀者可以學習,但是不應試圖將任何樣本代碼應用到實際應用環境中。微軟公司將不承擔任何可能由此造成的損失。
- 影響:在應用已有模式和識別反模式時,一些額外的其他的因素也必須考慮進來。
反模式#1:CRUD(Create、Read、Update、Delete)接口
- 上下文:
- 要求你為新的企業SOA項目設計網絡服務
- 問題:
- 你如何使用.Net來設計SOA服務
- 影響:
- 你是一個VB開發者并且知道如何創建構件
- 這是你的第一個SOA項目
- 其他平臺的應用也能使用你的服務
- 解決方法:
- 就像設計以前的構件接口一樣,來設計你的服務接口
- 創建一個服務,它能實現CRUD操作
- 列表1中列舉了樣本代碼的一部分
列表1:簡單的VB.Net CRUD服務
| <WebMethod()> _ <WebMethod()> _ <WebMethod()> _ <WebMethod()> _ <WebMethod()> _ |
- 征兆和結果:
- 接口設計要有類似于RPC的行為、調用創建和移位等功能,而不是發送一個定義明確的用來指示采取某個行動的消息。這違反了第一原則(良好的邊界定義)和第三原則(只被模式共享)。
- 接口有可能交互頻繁,因為用戶也許需要去調用兩三個方法來完成工作。
- 用一個提交來創建,當操作成功或失敗時,用戶可能不知怎么辦。設計服務時始終把用戶的期望考慮在內——用戶需要知道什么?
- CRUD操作在網路服務的分解上是一個錯誤級別。它也許在服務內或服務之間實現,但是不應該讓用戶直接接觸。這個例子就是說服務允許內部的實現出現在服務的公共接口中。
- 這個接口意味著正式的交互,比如列舉(查看MoveNext和Current方法)
- 抽象類型(比如Current方法返回的對象)導致一個很脆弱的協約,這是例子違反了第三原則(只被模式共享)。
- 如果服務在不一致的狀態下可以拋棄數據,那么服務是十分危險的。如果用戶增加一個新的訪問(或更新已經存在的訪問)并且沒有調用CommitChanges方法時,將會發生什么?正如先前提到的,服務提供商不能信任用戶始終能做出正確的事情。
以下列方式改變服務能避免上面列舉的這些風險條目:
- 用XML schema改變服務接口的通信。這個模式也包括了指定的服務行為(比如,New Contact Schema或者Change Contact Schema)。開發者應該優先考慮現有的行業標準而不是急于開發自己的模式,一個滿足你需要的模式也許已經存在。
- 在私有方法后隱藏對數據的操作,只有使用公共接口的模式才能訪問到。
- 確保用戶收到一個包含請求狀態信息的確認消息。
反模式#2:Loosey Goosey
- 上下文:
- 你能構建一個基于服務的解決方案
- 你的組織對服務重用很重視
- 問題:
- 如何使得服務接口有最大的靈活性和擴展性
- 影響:
- 你計劃去提供在解決方案的各個層次上的結合點
- 其他組需要訪問你的數據庫
- 解決方法:
- 將服務接口設計成Data Tiers(列表2中)
- 重視后期附加來設計高擴展性的接口
列表2. 簡單的VB.NET Data Tier服務
| <WebMethod()> _ <WebMethod()> _ |
- 征兆和結果
- 事實上沒有協約存在。用戶不知道如何使用服務(比如,什么是有效的命令、編碼規則等)。
- 接口允許其所發生的錯誤。協約既不清楚也具有很高的安全風險,很容易受SQL攻擊的影響。
- 協約沒有提供足夠的信息讓用戶知道如何使用服務,如果為了讓用戶知道如何使用服務而讓他必須看一些東西而不是服務的署名,那么應該要重新回顧一下服務的分解。
- 期望用戶能夠熟悉數據庫和表結構而不是使用網絡服務,這將導致服務提供商和用戶之間更緊密的耦合。
- 由于依靠后期的附加綁定和同一服務內邊界之間的編/解碼,服務的運行性能將會很差。
以下列方式改變服務能避免上面列舉的這些風險條目:
- 用定義明確XML schema改變服務接口的通信。這個模式不應該透露任何有關于潛在的數據倉庫的信息,這個模式的語義也應該能提供上下文給用戶,使得他們能理解服務的目的。(這個方法能提高服務的運行能力)
- 數據庫交互應該被封裝在私有方法中,不讓用戶知道數據庫和與之相關的表的細節。
- 確保用戶收到一個包含請求狀態信息的確認消息。
模式#1:文檔處理器(Document Processor)
這個模式的樣本代碼可以下載獲得
- 上下文:
- 構建一個基于服務的解決方法
- 與SO設計原則相吻合
- 問題:
- 如何創建一個簡單的定義明確的協約,并且與SO設計原則相吻合?
- 影響:
- 服務的協約應該支持以文檔為中心的考慮。
- 協約應該明確定義服務的語義(避免Loosey Goosey反模式)。
- 協約應該在服務本身中封裝其實現以支持松耦合(避免CRUD接口反模式)。服務協約是是一個邊界,它不應該泄漏任何有關內部實現的信息(記住第一原則)。
- 服務必須遵守WS-I Basic Profile,使得它能被任何支持網絡服務的平臺或編程語言使用。
- 服務應該作為一個完整單元的工作代表一個業務過程(避免一些假設,比如在CRUD接口反模式中)。還是那句話,服務提供商不能信任用戶能始終做出正確的事情,如果意外發生了,服務也不能依靠用戶來調用其他的服務去恢復或維修它。
- 解決方法:
- 定義并重用一個XML Schema來表示一個請求和響應消息,確保所有公共的互動使用這些模式。(列表3中有一個樣本代碼片斷)
- 直接從模式中產生對象以加快開發,這有時就是一種協約優先(Contact-First)的方法,你在開發實際的服務前定義服務的協約,此協約能被用來產生服務代碼。協約優先能減小互操作中的障礙,因為它建立在很多技術的經驗之上(CORBA、COM和DCE都使用接口語言)。網絡服務有時采用協約優先的方法,因為SOAP經常不用WSDL來解決簡單的問題。不管怎樣,許多開發環境提供了對協約優先的簡單支持,許多工具,比如WSCF和XSD Object Code Generator都能幫助進一步自動化此過程。
- 服務提供商不能期望用戶以具體的方式去調用和使用服務,這就是說,為一個已定的事務處理使用異常處理過程是合理的,但是服務提供商不能依靠用戶去觸發這個處理過程,一個保留模式(下面可見)為事務處理提供了最自治的保護,不需要用戶來進行操作。
列表3. Sample C# Document Processor Serivce
| [WebMethod()] public FindCustomerByCountryResponse FindCustomersByCountry( |
- 征兆和結果:
定義使用簡單模式的服務與SO設計原則相一致:
- 將一個以文檔為中心的網絡服務映射到一個業務過程是比較容易的,因為用戶傾向于將業務過程視為發送和接受文檔的思維方式(注意,那些文檔也許代表著業務事件和不真實的業務文檔)。
- 服務邊界作為一條臨界線來實現公共數據和私有數據之間的轉換。
- 服務的實現細節對用戶應該是不可見的。
- 采用協約優先的方法確保服務有很高的互操作性。
- 以文檔中心的服務更容易得到發展,因為所有的互操作都是通過消息而不是實際代碼的RPC來實現的。
這種實際做法中還有一些次要的東西需要注意:
- 由于需要將數據內部到外部的轉換,運行性能可能成為一個問題。
- 用戶必須將他們的數據表示轉換成服務所使用的模式,一些用戶也許能覺得映射到模式是一個很麻煩的過程。
模式#2:多重復消息(Idempotent Message)
這個模式的樣本代碼可以下載獲得
- 上下文:
- 與SO設計原則相符合
- 構建一個基于服務和事務上的解決方法
- 如果發生了同一個消息的多次遞交,服務必須能夠給予修正。
- 問題:
- 如何確保消息是重復的?
- 影響:
- 不能期望用戶太多。
- 你工作在一個事務的數據庫系統,并且它需要頻繁可靠的更新。
- 一個可信賴的消息平臺也不一定能解決問題,因為用戶總能發送同一請求的多個副本。
- 解決方法:
- 服務協約(模式)需要從用戶得到消息,以被一組工作識別器標識(此后就是一個UOW ID),舉例圖1。
- 協約不能讓UOW ID始終是唯一的。
- UOW ID代表著一個單元的工作,并且只被執行一次,不管有多少條有著相同UOW ID值的消息。
- 服務將使用UOW ID來決定一個單元的工作是否已經被執行,還是只是啟動它。有很多選項來處理與UOW IDs相關的已經完成的工作:
- 1. 返回緩存副本。
- 2. 再次發送消息,就好像第一次請求沒有接收到一樣。
- 3. 拋出異常和返回錯誤。
圖 1:多重復消息模式
- 征兆和結果:
多重復消息是一個較難的論題,處理重復UOW IDs的三個選項中每一個都有幾個考慮因素:
- 返回緩存副本——這個選項需要服務在一定的時間內保留一個緩存響應,決定時間值或緩存刷新應該被相關的業務過程所決定。這里還有一些其他的因素要考慮:
- ?當前值不同于緩存中的值嗎?
- ?如果響應是錯誤的,怎么辦?
- ?如果用戶重用UOW IDs去做不相關的工作,怎么辦?
- 再次發送消息——對于簡單的讀操作,這是無害的。但是對于寫操作,這無疑是可怕的(比如,付清帳單)。如果第一次處理UOW ID就發生了錯誤了,怎么辦?再處理一次可能導致相同的錯誤(這有時就是一個有害的消息循環)
- 拋出異常——用戶也許沒有收到服務的第一次響應并且簡單地再次重發了同一個UOW ID(可能由于一個響應超時了)。如果最后一次發送也丟失了,用戶將如何接收響應呢?
UOW ID應該作為響應模式的一部分,將重復請求的處理與相關業務過程聯系起來,UOW ID也可被加入成一個自定義的SOAP報頭,使得重復請求的處理成為整個消息處理基礎的一部分,URI應該被包含以幫助探測重復的進入,一系列的修正措施也應該被自動保留,使修正跟蹤能返回給UOW ID。最后,關于緩存刷新的論題,在響應被接收的時候反映響應時可被進一步討論,緩存管理是一個另外的難題,并且已經超出了本論文的范圍。
支持多重復消息機制將提高服務的自治性(第一原則),因為服務不再依靠用戶是否做正確的事情,還有一些方面需要注意的:
- 使用模式的服務將肯定會使用大量的存儲空間來容納緩存中的響應。
- 由于緩存的管理,勢必會對服務的運行性能造成較大的影響。
模式#3:預留(Reservation)
這個模式的樣本代碼可以下載獲得。
- 上下文:
- 與SO設計原則相符合。
- 有一個復雜的業務過程想要提供給用戶,這個業務過程在單個事務處理時需要幾個小時。
- 問題:
- 在一個長時間運行的過程中,如何保持數據的一致性?
- 影響:
- 分布式的事務處理不能被共享。
- 這樣的業務過程需要幾個消息才能完成。
- 消息交換所需要的時間從幾秒到幾個小時。
- 解決方法:
- ?消息創建試驗型的操作——這些試驗操作是原子級的事務處理,它能保證數據庫在一致的狀態。
- 三個試驗操作都有三種可能的輸出結果:
- 1. 試驗操作被證實(通過消息交換的完成)
- 2. 由于一次失誤或其他參與者有意識地操作,試驗操作被取消。
- 3. 試驗操作(消息交換)在期望的服務級別(時間范圍)內沒有完成。
- 必須定義一個對話機制去追蹤每個試驗操作的狀態,分配唯一的識別器(預留ID)和時間戳給每個對話可以使服務記住每個對話結束的位置。圖2說明了這種模式。
- 用戶試圖確認已注冊的預留時,必須包括一個有效的預留ID,如果確認請求缺少或無效的預留ID,那么此請求將被服務拒絕。
- 終止時間戳使服務在有效時間段后,終止不確定的預留。
圖2 預留模式
- 征兆和結果:
- 分配唯一的預留IDs和時間終止戳能確保服務和相關的業務規則處理數據一致性問題。
- 預留IDs用來跟蹤對話的狀態。
- 時間終止戳則以預定方式來處理超時和消息丟失。
預留ID和時間終止戳應該是驗證請求模式的一部分,將預留過程與實際業務過程聯系起來。對于每個預留請求,服務為之產生預留IDs又生成時間終止戳,使服務能周期性地檢查和終止不確定的預留,這種模式可以與多消息模式相結合,并進一步使服務與重復預留請求相隔離。
還有一些需要注意的方面:
- 處理預留請求的業務規則必須明確定義,預定過多將如何處理?
- 我們有點習慣于兩個階段的提交模型,并且經常試圖在不適合的地方應用它(比如,長時間運行過程)。長時間運行過程必須保持原子過程的一致性,在這種長時間運行過程中的工作隔離不是一個簡單的任務或一個模式,比如預留就是提到此論題的簡單嘗試。
總結
SO的四個原則提供了一系列基本的規則,它們能引導服務發展的方向,這篇論文所列舉的模式和反模式都用來說明這些原則如何對服務設計產生的影響,我們也提供一些額外的指導來確保你們將來的服務設計和發展方向將會取得成功:
- 當代收服務時,將業務過程在已有文檔和經確認的業務事件的基礎上進行模型化。
- 服務接口的靈活性時很重要的,但也要避免太過于靈活而導致服務協約變得模糊不清。
- 不要期望用戶始終做正確的事情,如果服務需要用戶按照先前定義好的方式去執行一系列的步驟,那么需要尋找一些模式(比如,預留)去維護加強這些步驟。
- 不能將服務和相關的資源處于不一致的狀態。
還有許多其他的設計理念與網絡服務相關,這系列中以后的論文將會闡述有關于版本更新、服務因素和策略驅動的服務配置等方面。
轉載于:https://www.cnblogs.com/jghhandsome/archive/2007/03/14/675042.html
總結
以上是生活随笔為你收集整理的SOA系列文章(二):服务设计原理:服务模式和反模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单稳定、前所未见!HUAWEI Pix
- 下一篇: 何时使用委托而不使用接口