一、SOA定義
SOA即面向服務架構(Service-Oriented Architecture)。在SOA中,一切皆服務。一個服務是通過消息交換來調用的程序,一個信息系統是共同完成一個特定任務的一組服務,SOA可以概述為一種由一組自治服務創建信息系統的方法。當然具體定義SOA真不好說,反正不能簡單理解為我們常用的開發技術如web服務、.net remoting或者wcf。
SOA設計四項基本原則:
1. 邊界必須明確
2. 服務必須自治
3. 服務分享 Schema 和 Contract, 而不是 Class
4. 由 Policy 決定服務間的兼容性
二、面向對象和面向服務
1、面向對象(OO)
a、應用於同一平台和運行環境
b、共享類型而不是Schema
c、采用便宜, 透明通信
d、對象的標識與生命周期由系統維護
e、客戶機和服務器的同步的部署
f、容易概念化,因而提供一個自然的模型
g、一般不需要狀態管理
h、應用於一個可預測的序列、期限和結果
i、目標是遠程透明地使用方法和類型
2、面向服務(SO)
a、應用於異構平台和運行環境
b、共享Schema而不是類型
c、采用高成本, 明確通信
d、服務是自治的: 安全和失效是隔離
e、允許的連續, 分離部署客戶機和服務器
f、基於軟件組件和分布的對象,依賴服務的協議
g、擁有並維護狀態
h、基於消息, 異步的, 以及長通信
i、目標是提供服務隔離和調用的標准
三、SOA反模式
1、CRUD型接口
寫應用的怎么能離得開CRUD?但是CRUD型接口暴露在SOA服務中往往意味着服務抽象不合理,這種類型接口帶來的不良后果:
a、鼓勵類似 RPC 的調用行為
RPC示例:
Serv.SendItemsToBePurchased(Array[] items); Serv.ShippingAddress(stringAddress); Serv.CheckOut();
消息示例:
//Client string msg= "<Items>...</Items>"; MyMethod(msg); //Server [WebMethod] void MyMethod(stringmsg){ . . . }
b、可能隱含帶會話狀態的交互
c、交互可能過於繁復
小結:CRUD型接口往往是抽象不合理的結果,它弱化了Contract,沒有按業務功能來實現服務。有人說CRUD可以讓調用方自己排列組合構造實現復雜服務,但是這種接口往往需要調用多次,而我們知道需要多個消息才能完成的操作是非常危險的,因為它很容易造成業務數據不一致。
2、數據層接口
這種服務以數據驅動,提供數據層DataTable、DataSet等重粒度的類型的返回,說到底還是抽象不合理造成的。這種接口的不良后果:
a、調用者隱性的依賴於你的服務的具體實現,結果將是緊耦合
b、Contract 沒有提供足夠的信息來使用這個服務
四、SOA服務設計指南
如何設計一個容易使用的、定義明確的接口?總體來說,你的接口必須:
(1)、鼓勵以文檔為中心的思考(特別提示: 如果你需要讀一些別的文檔才能調用現有接口,那你的服務分解可能不正確)
(2)、在 contract 中定義清晰的語義
(3)、通過對實現的封裝達到松耦合
(4)、可以方便的從任何平台上調用 (WS-I base profile)
(5)、用一個完整的工作單元代表一個業務流程
想要真正設計實現一個容易使用定義明確的接口並不容易,有幾個經驗可以指導我們的實際開發工作。
1、面向接口編程
服務接口抽象非常重要,接口抽象的同時要定義契約,定義schema,接口一旦確定以后很難修改,所以SOA接口抽象的好壞決定SOA服務的成敗。
對於一般的SOA服務,我們可以總結如下幾種類型的接口:
a、只讀(查詢)
b、只寫 (新增、修改和刪除)
c、讀和寫(CRUD的組合)
通常不論什么類型的接口,輸入參數不宜過多,參數組合或依賴關系不能過分啰嗦復雜;而對於返回信息則是越多越具體越好。
2、設計"大塊頭"的接口
服務必須封裝一個完全的工作單元,這種設計說明業務系統的實現細節被封裝了,不要指望調用方排列組合CRUD型接口正確完成一個業務功能。
SOA服務需要處理失敗,在一個服務內部必須要有完善的異常處理邏輯,在任何情況下操作失敗都不會將服務留在一個不一致的狀態,不能依賴於調用者調用另一個服務來修復錯誤。
a、服務的分解
• 以業務文檔驅動的業務流程模型
• 不要試圖使服務可擴展或靈活性
b、服務的自治性
• 任何事情都不要依賴於調用者
• 預期到調用者在使用你的服務時會犯錯誤
• 永遠不要讓服務處於一種不一致的狀態
個人所接觸過的某些SOA服務在實現的過程中從來沒有考慮過接口,因為CRUD慣了,很多人認為原子的操作就是萬能的接口。
3、避免共享分布式事務
對於大塊頭接口,在一個長的流程中如何保持數據的一致性?這種情況下某些業務可能需要用到分布式事務。但是在SOA服務實現中,應該避免分布式事務,而應優先考慮Reservation (預約保留)模式或者補償機制。
Reservation 模式提供了最自治的保護,Reservation 模式的好處:
a、數據的一致性依賴於服務和它的業務規則
b、一致的處理失敗情形 (超時,通訊中斷等)
Reservation 模式的問題:
a、你必須為reservation定義業務規則 ,系統有變得更加復雜的風險
b、進度中工作項的隔離不會像二次提交的事務(2PC)那樣自動化
當然我們還可以采用補償方式,但不要依賴於調用者來完成,實在不能系統自動解決則人工介入。
4、保證冪等性
重復消息是SOA服務實現中非常常見的問題,你永遠不要指望調用方每次請求消息不一樣,對於讀操作,重復消息可能無害,可對於寫操作很可能就是災難。Idempotent (冪等)模式用於處理重復的消息非常合適。Idempotent (冪等)模式的基本處理思路是:
a、調用者給消息一個唯一請求 ID 標識
• 服務的Ccontract 可以說明這是必須的
• ID 標識一個工作單元,這個工作單元只應執行一次
• 工作單元ID可以是Schema的一部分,也可以是一個定制的SOAP Header
b、接收者在執行一個工作單元必須先檢驗該工作單元是否已經執行過。檢查是否執行的邏輯通常是根據唯一請求 ID ,在服務端查詢請求是否有記錄,是否有對應的響應信息,如果有,直接把響應信息查詢后返回;如果沒有,那么就當做新請求去處理。
Idempotent 模式的好處:
a、服務的自治性增強了,不需要依賴於調用者作正確的事
Idempotent 模式的缺點:
a、需要為緩存響應消耗大量的存儲空間
b、需要為緩存管理付出性能的代價
5、避免共享Schema
一個很常見的問題,對於多個服務,應該用同樣的 Schema還是每個服務定義自己的 Schema?
你可能會說,服務實現后就非常穩定了,應該很少改動。所以一個共享Schema搞定,維護也只維護一份,這樣不是很爽嗎?
但是,業務變化是永恆的主題,服務也需要擁抱變化。你永遠也不能保證服務實現不會變化,也不能假設每次變化共享Schema只做增量永遠向下兼容。
所以,根據實際開發和維護經驗,最合理的做法是給每個服務定義自己的 Schema(哪怕現有的Schema暫時都是一模一樣的),因為共享 Schema 使得獨立變更每個服務比較困難,並給服務的調用者帶來不必要的麻煩。
Duplicate Is Evil,但服務實現中應該避免假設適當重復,這樣應對變化才能更加游刃有余,體會到這種好處需要開發認知能力提升到一定高度。
6、重視性能
需求即功能,性能也是一項功能。對於開發人員,沒有人願意自己開發的接口被人說性能差。我們應該在設計時就考慮性能,在項目開發的整個過程中要對服務性能進行反復測試,循序漸進改善系統性能。
主要有兩種量化SOA服務性能的方法:
a、機器吞吐率 (requests/sec)
b、響應時間 (time to first/last bytes)
優化服務性能的方法通常有如下幾種:
a、減少本地數據IO,盡量減少調用本地的web服務以減少網絡IO;
b、盡量使用原始數據類型參數,避免在調用之間維護服務器狀態;
c、考慮對服務的結果進行緩存;
d、要有批量多次的思路,選擇適用的大數據包傳送方式,或者對結果進行壓縮;
e、異步處理,但會增加開發和維護難度。
7、其他
a、避免循環調用
服務之間相互引用,或者幾個服務之間隱式地構成閉環調用,這種情況會引發無意識的遞歸造成系統崩潰。
b、重視安全性
服務調用的安全性也是一個問題。可以通過ESB實現服務注冊、角色授權、權限分配等基本安全控制,但是仍不能保證傳輸和調用安全性。可以采用一些技術解決方案增強安全性,比如微軟的WSE(Web Services Enhancements)。
參考:
<<SOA服務設計原則>>
<<Understanding SOA with Web Services(中文版)>>