微觀SOA:服務設計原則及其實踐方式(下篇)
在上一篇文章中,我說到SOA是一個特別大的話題,不但沒有絕對統一的原則,而且很多原則本身的內容也具備相當模糊性和寬泛性。雖然我們可以說SOA ≈ 模塊化開發 + 分布式計算,但由于其原則的模糊性,我們仍然很難說什么應用是絕對符合SOA的,只能識別出哪些是不符合SOA的。
本篇將對8種可操作的服務設計原則進行細化的分析,作為SOA實踐的參考。
服務設計原則1:優化遠程調用
這里的遠程調用特指RPC(Remote Procedure Call)。當然更面向對象的說法應該是遠程方法調用或者遠程服務調用等等。
由于SO接口通常要被遠程訪問,而網絡傳輸,對象序列化/反序列化等開銷都遠遠超過本地Object訪問幾個數量級,所以要加快系統的響應速度、減少帶寬占用和提高吞吐量,選擇高性能的遠程調用方式經常是很重要的。
但是遠程調用方式往往又要受限于具體的業務和部署環境,比如內網、外網、同構平臺、異構平臺等等。有時還要考慮它對諸如分布式事務,消息級別簽名/加密,可靠異步傳輸等方面的支持程度(這些方面通常被稱為SLA:service level agreement),甚至還包括開發者的熟悉和接受程度等等。
因此,遠程調用方式往往需要根據具體情況做出選擇和權衡。
以Java遠程Service為例分析不同場景下,傳輸方式的某些可能較好選擇:
- 內網 + 同框架Java客戶端 + 大并發:多路復用的TCP長連接 + kryo (二進制序列化) (kryo也可以用Protostuff,FST等代替)
- 內網 + 不同框架Java客戶端:TCP + Kryo
- 內網 + Java客戶端 + 2PC分布式事務:RMI/IIOP (TCP + 二進制)
- 內網 + Java客戶端 + 可靠異步調用:JMS + Kryo (TCP + 二進制)
- 內網 + 不同語言客戶端:thrift(TCP + 二進制序列化)
- 外網 + 不同語言客戶端 + 企業級特性:HTTP + WSDL + SOAP (文本)
- 外網 + 兼顧瀏覽器、手機等客戶端:HTTP + JSON (文本)
- 外網 + 不同語言客戶端 + 高性能:HTTP + ProtocolBuffer (二進制)
簡單來說,從性能上講,tcp協議 + 二進制序列化更適合內網應用。從兼容性、簡單性上來說,http協議 + 文本序列化更適合外網應用。當然這并不是絕對的。另外,tcp協議在這里并不是限定遠程調用協議一定只能是位于OSI網絡模型的第四層的原始tcp,它可以包含tcp之上的任何非http協議。
所以,回答上面提到的問題,WebServices (經典的WSDL+SOAP+HTTP)雖然是最符合前述SOA設計原則的技術,但并不等同于SOA,我認為它只是滿足了SOA的底線,而未必是某個具體場景下的最佳選擇。這正如一個十項全能選手在每個單項上是很難和單項冠軍去競爭的。更理想的SOA Service最好能在可以支持WebServices的同時,支持多種遠程調用方式,適應不同場景,這也是Spring Remoting,SCA,Dubbo,Finagle等分布式服務框架的設計原則。
遠程調用技術解釋:HTTP + JSON適合SOA嗎?
JSON簡單易讀,通用性極佳,甚至能很好支持瀏覽器客戶端,同時也常被手機APP使用,大有取代XML之勢。
但JSON本身缺乏像XML那樣被廣泛接受的標準schema,而一般的HTTP + JSON的遠程調用方式也缺乏像Thrift,CORBA,WebServices等等那樣標準IDL(接口定義語言),導致服務端和客戶端之間不能形成強的服務契約,也就不能做比如自動代碼生成。所以HTTP + JSON在降低了學習門檻的同時,可能顯著的增加復雜應用的開發工作量和出錯可能性。
例如,新浪微博提供了基于HTTP + JSON的Open API,但由于業務操作比較復雜,又在JSON上封裝實現了各種語言的客戶端類庫,來減少用戶的工作量。
為了解決這方面的問題,業界有很多不同方案來為HTTP + JSON補充添加IDL,如RSDL、JSON-WSP、WADL、WSDL 2.0等等,但事實上它們的接受度都不太理想。
另外值得一提的是,JSON格式和XML一樣有冗余,即使做GZIP壓縮之類的優化,傳輸效率通常也不如很多二進制格式,同時壓縮、解壓還會引入額外的性能開銷。
遠程調用技術解釋:Apache Thrift多語言服務框架
Thrift是最初來自facebook的一套跨語言的service開發框架,支持C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, JavaScript, Node.js, Smalltalk, Delphi等幾乎所有主流編程語言,具有極好的通用性。
Thrift被facebook,twitter等巨頭以及開源社區都廣泛使用,是非常成熟的技術。
Thrift的服務契約通過類似如下形式的IDL定義:
struct User { 1: i32 id, 2: string name, 3: string password } service UserService { void store(1: User user), UserProfile retrieve(1: i32 id) }
非常類似于C語言,易讀易寫,比WSDL簡單明了得多。比用java之類的編程語言也更方便,有時候可以把所有相關的接口和數據結構定義放到同一個文件,發布出去的時候不用再打一個壓縮包之類,甚至可以直接粘貼到文檔中
Thrift還提供工具,可以基于IDL自動生成各種語言對應的服務端和客戶端代碼:
[lishen@dangdang thrift]thrift --gen java user.thrift [lishen@dangdang thrift]$ thrift --gen cpp user.thrift [lishen@dangdang thrift]$ thrift --gen php user.thrift [lishen@dangdang thrift]$ thrift --gen csharp user.thrift
我認為thrift是比WebServices更簡單高效的技術,是在SOA中對WebServices最具有替代性的技術之一。
遠程調用技術解釋:多路復用的TCP長連接
這是一種追求極致高性能高伸縮的方式,這里只做簡要介紹。
比較典型的是twitter的Mux RPC協議以及google的SPDY協議,在其中多個請求同時共用同一個長連接,即一個連接交替傳輸不同請求的字節塊。它既避免了反復建立連接開銷,也避免了連接的等待閑置從而減少了系統連接總數,同時還避免了TCP順序傳輸中的線頭阻塞(head-of-line blocking)問題。
另外,國內比較著名的開源dubbo框架的默認RPC協議,以及業界許多小型開源RPC框架也都是類似的思路。
采用多路復用機制后,一般就要求服務器端和客戶端都支持額外的類似于會話層(即OSI網絡模型第六層)的語義,導致它們必須要依賴于同一套RPC框架。
其他很多RPC機制都是使用TCP短連接。即使有些RPC使用了長連接,但一個連接同一時間只能發送一個請求,然后連接就處于閑置狀態,來等待接收該請求的響應,待響應完畢,該連接才能被釋放或者復用。
HTTP 1.1也支持一種基于pipeline模式的長連接,其中多個HTTP請求也可共用一個連接,但它要求響應(response)也必須按照請求(request)的順序傳輸返回,即FIFO先進先出。而在完全多路復用的連接中,哪個的響應先ready就可以先傳輸哪個,不用排隊。
當然,短連接、長連接和多路復用長連接之間不存在絕對的好壞,需要取決于具體業務和技術場景,在此不詳細展開了。
遠程調用技術解釋:Java高效序列化
最近幾年,各種新的Java高效序列化方式層出不窮,不斷刷新序列化性能的上限,例如Kryo,FST等開源框架。它們提供了非常高效的Java對象的序列化和反序列化實現,相比JDK標準的序列化方式(即基于Serializable接口的標準序列化,暫不考慮用諸如Externalizable接口的定制序列化),在典型場景中,其序列化時間開銷可能縮短20倍以上,生成二進制字節碼的大小可能縮減4倍以上。
另外,這些高效Java序列化方式的開銷也顯著少于跨語言的序列化方式如thrift的二進制序列化,或者JSON等等
遠程調用技術解釋:RMI/IIOP和分布式事務
RMI/IIOP是Java EE中標準的遠程調用方式,IIOP是CORBA的協議,只有IIOP上的RMI才支持兩階段提交的分布式事務,同時提供和CORBA的互操作。
當然,嚴格的兩階段提交事務并不高效,還可能嚴重影響系統伸縮性甚至可用性等等,一般只應用在非常關鍵的業務中。
遠程調用技術解釋:Google ProtocolBuffer跨語言序列化
ProtocolBuffer是google開發的跨語言的高效二進制序列化方式,其序列化性能和thrift比較類似。事實上thrift最初就是ProtocolBuffer的仿制品。但它和thrift最大的不同是他沒有自帶的RPC實現(因為google沒有將RPC部分開源,但有大量第三方實現)。
由于不和RPC方式耦合,反而使得ProtocolBuffer能被方便的集成進大量已有的系統和框架中。在國內它也被百度、淘寶等廣泛的應用在Open API中,和HTTP搭配作為一種高效的跨平臺跨組織的集成方式。
服務設計原則2:消除冗余數據
同樣由于service的遠程調用開銷很高,所以在它的輸入參數和返回結果中,還要盡量避免攜帶當前業務用例不需要的冗余的字段,來減少序列化和傳輸的開銷。同時,去掉冗余字段也可以簡化接口,避免給外部用戶帶來不必要的業務困惑。
比如article service中有個返回article list的方法
List<Article> getArticles(...)
如果業務需求僅僅是要列出文章的標題,那么在返回的article中就要避免攜帶它的contents等等字段。
這里經典解決方案就是引入OO中常用的Data Transfer Object (DTO)模式,專門針對特定service的用例來定制要傳輸的數據字段。這里就是添加一個AriticleSummary的額外數據傳輸對象:
List<ArticleSummary> getArticleSummaries(...)
額外的DTO確實是個麻煩,而一般OO程序通常則可直接返回自己的包含冗余的業務模型。
服務設計原則3:粗粒度契約
同樣由于遠程調用開銷高,同時service的外部使用者對特定業務流程的了解也比不上組織內部的人,所以service的契約(接口)通常需要是粗粒度的,其中的一個操作就可能對應到一個完整的業務用例或者業務流程,這樣既能減少遠程調用次數,同時又降低學習成本和耦合度。
而OO接口通常可以是非常細粒度的,提供最好的靈活性和重用性。
例如,article service支持批量刪除文章,OO接口中可以提供
deleteArticle(long id)
供用戶自己做循環調用(暫不考慮后端SQL之類優化),但SO接口中,則最好提供
deleteArticles(Set<Long> ids)
供客戶端調用,將可能的N次遠程調用減少為一次。
例如,下訂單的用例,要有一系列操作
addItem -> addTax -> calculateTotalPrice -> placeOrder
OO中我們完全可以讓用戶自己來靈活選擇,分別調用這些細粒度的可復用的方法。但在SO中,我們需要將他們封裝到一個粗粒度的方法供用戶做一次性遠程調用,同時也隱藏了內部業務的很多復雜性。另外,客戶端也從依賴4個方法變成了依賴1個方法,從而大大降低了程序耦合度。
順便值得一提的是,如果上面訂單用例中每個操作本身也是遠程的service(通常在內網之中),這種粗粒度封裝就變成了經典的service composition(服務組合)甚至service orchestration(服務編排)了。這種情況下粗粒度service同樣可能提高了性能,因為對外網客戶來說,多次跨網的遠程調用變成了一次跨網調用 + 多次內網調用。
對這種粗粒度service封裝和組合,經典解決方案就是引入OO中常用的Facade模式,將原來的對象屏蔽到專門的“外觀”接口之后。同時,這里也很可能要求我們引入新的service參數/返回值的數據結構來組合原來多個操作的對象模型,這就同樣用到前述的DTO模式。
一個簡單Facade示例(FooService和BarService是兩個假想的本地OO service,façade將它們的結果值組合返回):
class FooBarFacadeImpl implements FooBarFacade { private FooService fooService; private BarService barService; public FooBarDto getFooBar() { FooBarDto fb = new FooBarDto(); fb.setFoo(fooService.getFoo()); fb.setBar(barService.getBar()); return fb; } }
當然,有的時候也可以不用facade和DTO,而在是FooService和BarService之外添加另一個本地service和domain model,這要和具體業務場景有關。
服務設計原則4:通用契約
由于service不假設用戶的范圍,所以一般要支持不同語言和平臺的客戶端。但各種語言和平臺在功能豐富性上有很大差異,這就決定了服務契約必須取常見語言、平臺以及序列化方式的最大公約數,才能保證service廣泛兼容性。由此,服務契約中不能有某些語言才具備的高級特性,參數和返回值也必須是被廣泛支持的較簡單的數據類型(比如不能有對象循環引用)。
如果原有的OO接口不能滿足以上要求,則在此我們同樣需要上述的Facade和DTO,將OO接口轉換為通用的SO契約。
例如原有對象模型
class Foo { private Pattern regex; }
Pattern是Java特有的預編譯好的,可序列化的正則表達式(可提高性能),但在沒有特定框架支持下,可能不好直接被其他語言識別,所以可添加DTO:
class FooDto { private String regex; }
服務設計原則5:隔離變化
雖然OO和SO都追求低耦合,但SO由于使用者范圍極廣,就要求了更高程度的低耦合性。
比如前述的article service,OO中可以直接返回article對象,而這個article對象在OO程序內部可能做為核心的建模的domain model,甚至作為O/R mapping等等。而在SO如果還直接返回這個article,即使沒有前面所說的冗余字段,復雜類型等問題,也可能讓外部用戶與內部系統的核心對象模型,甚至O/R mapping機制,數據表結構等等產生了一定關聯度,這樣一來,內部的重構經常都會可能影響到外部的用戶。
所以,這里再次對Facade和DTO產生了需求,用它們作為中介者和緩沖帶,隔離內外系統,把內部系統變化對外部的沖擊減少到最小程度。
服務設計原則6:契約先行
Service是往往涉及不同組織之間的合作,而按照正常邏輯,兩個組織之間合作的首要任務,就是先簽訂明確的契約,詳細規定雙方合作的內容,合作的形式等等,這樣才能對雙方形成強有力的約束和保障,同時大家的工作也能夠并行不悖,不用相互等待。因此SOA中,最佳的實踐方式也是契約先行,即先做契約的設計,可以有商務,管理和技術等不同方面的人員共同參與,并定義出相應的WSDL或者IDL,然后在開發的時候再通過工具自動生成目標語言的對應代碼。
對于WSDL來說,做契約先行的門檻略高,如果沒有好的XML工具很難手工編制。但對于Thrift IDL或者ProtocolBuffer等來說,由于它們和普通編程語言類似,所以契約設計相對是比較容易的。另外,對于簡單的HTTP + JSON來說(假設不補充使用其他描述語言),由于JSON沒有標準的schema,所以是沒法設計具有強約束力的契約的,只能用另外的文檔做描述或者用JSON做輸入輸出的舉例。
但是,契約先行,然后再生成服務提供端的代碼,畢竟給service開發工作帶來了較大的不便,特別是修改契約的時候導致代碼需要重寫。因此,這里同樣可能需要引入Facade和DTO,即用契約產生的都是Facade和DTO代碼,它們負責將請求適配和轉發到其他內部程序,而內部程序則可以保持自己的主導性和穩定性。
另外,契約先行可能會給前面提到的多遠程調用支持帶來一些麻煩。
當然契約先行也許并不是能被廣泛接受的實踐方式,就像敏捷開發中“測試先行”(也就是測試驅動開發)通常都是最佳實踐,但真正施行的團隊卻非常之少,這方面還需要不斷摸索和總結。但我們至少可以認為Echo中Java2WSDL并不被認為是SOA的最佳實踐。
服務設計原則7:穩定和兼容的契約
由于用戶范圍的廣泛性,所以SO的服務契約和Java標準API類似,在公開發布之后就要保證相當的穩定性,不能隨便被重構,即使升級也要考慮盡可能的向下兼容性。同時,如果用契約先行的方式,以后頻繁更改契約也導致開發人員要不斷重做契約到目標語言映射,非常麻煩。
這就是說SO對契約的質量要求可能大大高于一般的OO接口,理想的情況下,甚至可能需要專人(包括商務人員)來設計和評估SO契約(不管是否用契約先行的方式),而把內部的程序實現交給不同的人,而兩者用Facade和DTO做橋梁。
服務設計原則8:契約包裝
前述原則基本都是針對service提供端來講的,而對service消費端而言,通過契約生成對應的客戶端代碼,經常就可以直接使用了。當然,如果契約本身就是Java接口之類(比如在Dubbo,Spring Remoting等框架中),可以略過代碼生成的步驟。
但是,service的返回值(DTO)和service接口(Facade),可能被消費端的程序到處引用到。
這樣消費端程序就較強的耦合在服務契約上了,如果服務契約不是消費端定義的,消費端就等于把自己程序的部分主導權完全讓渡給了別人。
一旦契約做更改,或者消費端要選擇完全不同的service提供方(有不同的契約),甚至改由本地程序自己來實現相關功能,修改工作量就可能非常大了。
另外,通過契約生成的客戶端代碼,經常和特定傳輸方式是相關的(比如webservices stub),這樣給切換遠程調用方式也會帶來障礙。
因此,就像在通常應用中,我們要包裝數據訪問邏輯(OO中的DAO或者Repository模式),或者包裝基礎服務訪問邏輯(OO中的Gateway模式)一樣,在較理想的SOA設計中,我們也可以考慮包裝遠程service訪問邏輯,由于沒有恰當的名稱,暫時稱之為Delegate Service模式,它由消費端自己主導定義接口和參數類型,并將調用轉發給真正的service客戶端生成代碼,從而對它的使用者完全屏蔽了服務契約,這些使用者甚至不知道這個服務到底是遠程提供的的還是本地提供的。
此外,即使我們在消費端是采用某些手工調用機制(如直接構建和解析json等內容,直接收發JMS消息等等),我們同樣可以用delegate service來包裝相應的邏輯。
delegate service示例1:
// ArticlesService是消費端自定義的接口 class ArticleServiceDelegate implements ArticlesService { // 假設是某種自動生成的service客戶端stub類 private ArticleFacadeStub stub; public void deleteArticles(List<Long> ids) { stub.deleteArticles(ids); } }
delegate service示例2:
// ArticlesService是消費端自定義的接口 class ArticleServiceDelegate implements ArticlesService { public void deleteArticles(List<Long> ids) { // 用JMS和FastJson手工調用遠程service messageClient.sendMessage(queue, JSON.toJSONString(ids)); } }
從面向對象到面向服務,再從面向服務到面向對象
總結上面的幾個原則,雖然只是談及有限的幾個方面,但大致也可看出OO和SO在實際的設計開發中還是有不少顯著的不同之處,而且我們沒有打算用SO的原則來取代過去的OO設計,而是引入額外的層次、對象和OO設計模式,來補充傳統的OO設計。
其實就是形成了這種調用流程:
-
service提供端:OO程序 <- SOA層(Facade和DTO)<- 遠程消費端
-
service消費端:OO程序 -> Delegate Service -> SOA層(Facade和DTO 或者 其他動態調用機制)-> 遠程提供端
Facade、DTO和Delegate Service負責做OO到SO和SO到OO的中間轉換。
現在,可以回答Echo示例中的問題:通過“透明的”配置方式,將OO程序發布為遠程Service,雖然可能較好的完成了從本地對象到遠程對象的跨越,但通常并不能較好的完成OO到SO的真正跨越。
同時,透明配置方式也通常無法直接幫助遺留應用(如ERP等)轉向SOA。
當然,在較為簡單和使用范圍確定很有限應用(比如傳統和局部的RPC)中,透明式遠程service發布會帶來極大的便利。
另外,上面對SO的所有討論都集中在RPC的方式,其實SO中也用message的方式做集成,它也是個大話題,暫時不在此詳論了。
為什么不能放棄面向對象?
SO是有它的特定場景的,比如遠程的,范圍不定的客戶端。所以它的那些設計原則并不能被借用來指導一般性的程序開發,比如很多OO程序和SO原則完全相反,經常都要提供細粒度接口和復雜參數類型以追求使用的使用靈活性和功能的強大性。
就具體架構而言,我認為SOA層應該是一個很薄的層次(thin layer),將OO應用或者其他遺留性應用加以包裝和適配以幫助它們面向服務。其實在通常的web開發中,我們也是用一個薄的展現層(或者叫Web UI層之類)來包裝OO應用,以幫助它們面向瀏覽器用戶。因此,Façade、DTO等不會取代OO應用中核心的Domain Model、Service等等 (這里的service是OO中service,未必是SO的)。
綜合起來,形成類似下面的體系結構:
理想和現實
需要特別指出的是,上面提到的諸多SO設計原則是在追求一種相對理想化的設計,以達到架構的優雅性,高效性,可重用性,可維護性,可擴展性等等。
而在現實中任何理論和原則都可能是需要作出適當妥協的,因為現實是千差萬別的,其情況遠比理論復雜,很難存在放之四海而皆準的真理。
而且很多方面似乎本來也沒有必要追求完美和極致,比如如果有足夠能力擴充硬件基礎設施,就可以考慮傳輸一些冗余數據,選擇最簡單傳輸方式,并多來幾次遠程調用等等,以減輕設計開發的工作量。
那么理想化的原則就沒有意義了嗎?比如領域驅動設計(Domain-Driven Design)被廣泛認為是最理想的OO設計方式,但極少有項目能完全采用它;測試驅動開發也被認為是最佳的敏捷開發方式,但同樣極少有團隊能徹底采用它。但是,恐怕沒有多少人在了解它們之后會否認它們巨大的意義。
理想化的原則可以更好的幫助人們理解某類問題的本質,并做為好的出發點或者標桿,幫助那些可以靈活運用,恰當取舍的人取得更大的成績,應付關鍵的挑戰。這正如孔子說的“取乎其上,得乎其中;取乎其中,得乎其下;取乎其下,則無所得矣”。
另外,值得一提的是,SOA從它的理念本身來說,就帶有一些的理想主義的傾向,比如向“全世界”開放,不限定客戶端等等。如果真愿意按SOA的路徑走,即使你是個土豪,偷個懶比浪費網絡帶寬重要,但說不定你的很多用戶是土鱉公司,浪費幾倍的帶寬就大大的影響他們的利潤率。
延伸討論:SOA和敏捷軟件開發矛盾嗎?
SOA的服務契約要求相當的穩定性,一旦公開發布(或者雙方合同商定)就不應該有經常的變更,它需要對很多方面有極高的預判。而敏捷軟件開發則是擁抱變化,持續重構的。軟件設計大師Martin Fowler把它們歸結為計劃式設計和演進式設計的不同。
計劃理論(或者叫建構理論)和演進理論是近代哲學的兩股思潮,影響深遠,派生出了比如計劃經濟和市場經濟,社會主義和自由主義等等各種理論。
但是,計劃式設計和演進式設計并不絕對矛盾,就像計劃經濟和市場經濟也不絕對矛盾,非此即彼,這方面需要在實踐中不斷摸索。前面我們討論的設計原則和架構體系,就是將SOA層和OO應用相對隔離,分而治之,在SOA層需要更多計劃式設計,而OO應用可以相對獨立的演進,從而在一定程度緩解SOA和敏捷開發的矛盾。
延伸討論:SOA和REST是一回事嗎?
從最本質的意義上講,REST(Representational State Transfer)實際是一種面向資源架構(ROA),和面向服務架構(SOA)是有根本區別的。
例如,REST是基于HTTP協議,對特定資源做增(HTTP POST)、刪(HTTP DELETE)、改(HTTP PUT)、查(HTTP GET)等操作,類似于SQL中針對數據表的INSERT、DELETE、UPDATE、SELECT操作,故REST是以資源(資源可以類比為數據)為中心的。而SOA中的service通常不包含這種針對資源(數據)的細粒度操作,而是面向業務用例、業務流程的粗粒度操作,所以SOA是以業務邏輯為中心的。
但是在實際使用中,隨著許多REST基本原則被不斷突破,REST的概念被大大的泛化了,它往往成為很多基于HTTP的輕量級遠程調用的代名詞(例如前面提到過的HTTP + JSON)。比如,即使是著名的Twitter REST API也違反不少原始REST的基本原則。
在這個泛化的意義上講,REST也可以說是有助于實現SOA的一種輕量級遠程調用方式。
SOA架構的進化
前面討論的SOA的所有問題,基本都集中在service本身的設計開發。但SOA要真正發揮最大作用,還需要不斷演進成更大的架構(也就是從微觀SOA過渡到宏觀SOA),在此略作說明:
-
第一個層次是service架構:開發各種獨立的service并滿足前面的一些設計原則,我們前面基本都集中在討論這種架構。這些獨立的service有點類似于小孩的積木。
-
第二個層次是service composition(組合)架構:獨立的service通過不同組合來構成新的業務或者新的service。在理想情況下,可以用一種類似小孩搭積木的方式,充分發揮想象力,將獨立的積木(service)靈活的拼裝組合成新的形態,還能夠自由的替換其中的某個構件。這體現出SOA高度便捷的重用性,大大提高企業的業務敏捷度。
-
第三個層次是service inventory(清單)架構:通過標準化企業服務清單(或者叫注冊中心)統一的組織和規劃service的復用和組合。當積木越來越多了,如果還滿地亂放而沒有良好的歸類整理,顯然就玩不轉了。
-
第四個層次是service-oriented enterprise架構……
總結
至此,我們只是簡要的探討了微觀層面的SOA,特別是一些基本設計原則及其實踐方式,以期能夠略微展示SOA在實踐中的本質,以有助于SOA更好的落地,進入日常操作層面。
最后,打個比方:SOA不分貴賤(不分語言、平臺、組織),不遠萬里(通過遠程調用)的提供服務(service),這要求的就是一種全心全意為人民服務的精神……