Web服務和SOA(一)

翻譯自<<Service Oriented Architecture with Java>>(使用Java開發面向服務的架構)一書之第二章

本章我們將詳細講述SOA的實現,並開始我們的Web服務實踐之旅。在本章中,我們將會看到,爲什麼在企業交互的環境中,XML是消息交換的正確選擇。接下來我們開始定義一個示例的Web服務,採用自頂向下的方法來開發我們第一個Web服務。然後我們再採用頗爲通用的兩種傳輸協議改進我們的程序。

通過對Web服務兩種風格(RPCDocument)的比較,我們可以看到採用Document風格的Web服務更加完善,並被廣泛使用。在本章的最後,我們將簡單介紹一下最流行的幾種Web服務實現框架。

SOA的實施方法

實施SOA的第一步其實很簡單,即:確定您的應用所包含的業務功能。下面我們就仔細分析這句話的含義:

(1)     確定:就是要找到軟件組成的獨立模塊,這些模塊是自包含的,並且從功能上說,它們是不可分割的(具有功能上的原子性)。這就要求我們在設計時,要把您的問題域按照軟件調用規範切分成良好定義的模塊,同時也要確定這些模塊的邊界。對業務模塊調用者而言,他們要按照契約式軟件規範來正確調用這些模塊。在軟件設計時,請牢記一條,在多個環境(項目)中都能使用的一個模塊就是軟件開發中的金塊。從某種意義上說,確定出軟件中的業務功能,就是一種更高層次的抽象,它將我們以前對“接口”的抽象提高到業務層面上。

(2)     業務功能:這個詞是指在SOA實踐中,我們將專注於業務層(MVC概念中的M),而不是表示層和控制層(MVC模式中的VC)。我們在這裏只討論服務,而良好設計的服務應該和表示層相互獨立,服務對錶現它們的表示層一無所知。

(3)     應用所包含的:應用程序可能包含許多軟件層,但我們這裏的應用程序強調的是多個而非僅僅一個應用程序。這是SOA實施方法的一個“大躍進”,它超越了我們現在正在進行的項目,我們只需做少量的工作,就可以讓SOA中的業務組件超越單一項目,它們可以非常容易地使用在以後的應用程序中。

現在,假定我們按照上面的設計方法設計出了我們所需的服務,下一步我們該做什麼呢,我們又如何實現它們呢?

我們不妨先舉一個服務的例子,這個例子將返回所有的客戶列表,它不需要輸入參數,並返回一串對象列表。

服務的消費者(例如用戶界面)如何能調用到該服務,並得到它所請求的對象列表呢?這可以通過多種方式實現,下面是其中最爲流行的幾種:

(1)     使用本地調用:就Java而言,本地調用可以通過RMI(遠程方法調用)SocketsServletsJMS來實現;

(2)     使用分佈式對象交換中間層:例如服務消費者可使用CORBADCOM來調用服務;

(3)     使用基於文本的交換協議來調用服務:服務消費者可以發送基於文本流的請求,然後可獲得包含數據的應答文本。這是Web服務實現的基礎。

第一種方法非常直接,當它有一些缺陷,它必須依賴於一種語言,服務及其消費者都必須使用同一種語言,比如Java.Net等,並且,服務及其消費者交換對象的版本還必須相同;否則,對象傳輸就會失敗。

分佈式對象傳輸在相當長的時間內非常成功,尤其是CORBA的跨平臺特性爲後來的可互操作性提供了奠定了良好的基礎。

第三種基於文本交換的實現方法表明,在客戶端和服務器端都需要進行序列化和反序列化操作。但客戶端發送請求時,需要將對象序列化成文本格式,然後發送;但服務器收到請求後,它需要將文本經過序列化轉換成對象。同樣的過程也發送在服務應答的流程中。序列化和反序列化貌似增加了對象交換的複雜性,其實不然,請想想這樣做的優勢:軟件能完全獨立於技術,實現了軟件之間的鬆散耦合。

將數據嵌入到文本中最自然的方法莫過於通過XML來實現。

XML的優點和不足

XML語言由W3C1998年爲了數據交換的目的而設計的。對着時間的推移,該語言的魅力也開始顯現。XML的主要優點有以下四個方面:

                具有結構化特性
               
是可移動的
               
具可擴展性
               
具文本格式

XML的不足

以樹型結構組織的XML語言優勢可能有一些缺點。例如,XML在表示共享的對象應用方面有些不足,所以人們通常爭論,XML是否是表示一個任意對象的最佳選擇。想想我們前面舉的例子,假設您在倫敦有許多客戶,使用XML表示這些客戶列表時,就會產生數據冗餘。這是因爲在客戶這個XML實體中,其城市屬性值都相同。這是人們所不能接受的。其實,這個例子恰恰反證了人們對於XML的誤用。在上例中,城市這個屬性應該算作一個實體而非屬性。這個問題的更好的解決方法是,向關係數據庫學習,把重複的數據搬移到主要對象之外,在主要對象中只應用這些重複對象的ID號即可。

客戶端可以使用有狀態方法(Stateful Approach),首先得到城市這個實體的列表。當它調用getAllCustomers服務時,服務器返回的客戶列表的城市屬性只需ID即可,而不需完整的城市名,返回的示例客戶列表如清單1所示:

程序清單1—有狀態方法(Stateful Approach)

<Customers>

<customer>

          <id>4</id>

<name>Smith Ltd</name>

<location>

<address>39, Kensington Rd.</address>
<city>LND</city>

</location>

</customer>

<customer>

          <id>7</id>

<name> Merkx & Co.</name>

<location>

<address>39, Venice Blvd.</address>
<city>LAX</city>

</location>

</customer> <id>7</id>

...

</Customers>

 

而且,我們還可以採用無狀態方法(Stateless Approach),實現自包含的服務,即在我們的XML應答中,嵌入所需要的所有數據對象。示例代碼如清單2所示:

程序清單2—無狀態方法(Stateless Approach)

<Entireresponse>

<cities>

<city>

<id>LND</id>

<name>London</name>

<country>UK</country>

</city>

<city>

<id>LAX</id>

<name>Los Angeles</name>

<country>USA</country>

</city>

</cities>

<customers>

<customer>

<id>4</id>

...

<location>

...

<city>LND</city>

</location>

</customer>

<customer>

<id>7</id>

...

<location>

...
<city>LAX</city>

</location>

</customer>

...

</customers>

</Entireresponse>

Web服務、REST風格的服務及其它與XML傳輸相關的技術介紹

在上節中,我們討論了使用XML實現SOA有哪些優勢,但並沒有說明SOA就是由Web服務構成的。SOAWeb服務有時在同一場閤中被混用,因而人們對此產生了一些誤解。SOA是一種方法,是架構設計中的一種選擇,它與技術和語言無關。在面向服務的架構中,服務並不是Web服務的簡稱,而是一種寬泛意義上的服務。我們可以在不考慮任何具體技術實現的前提下,設計出某種獲得所有商品列表的服務來。

按照SOA進行設計的意思爲,針對某一特定的業務領域,設計出符合業務規則的高層接口。當然,但產品或項目進行到某一階段時,我們必須要選擇具體的實現方式。下面我們就討論一下SOA有哪些實現方式,從比較簡單的手工方法,到業界廣泛採用的技術標準(SOAP)

在進一步討論之前,我們先了解一下本書中涉及到的幾個術語。協議(Protocol)這個詞在本書中的意思會依據上下文有所不同,其中一個關鍵是要區別“傳輸協議(亦稱網絡協議)(Transportation or layer protocol)”和“交換協議(Communication protocol)”。傳輸(網絡)協議是指傳輸信息所使用的網絡協議,它可以是廣泛使用的HTTP協議,也可以是允許異步傳輸的SMTP協議或JMS協議;而交換協議需要考慮如何把消息放到XML文檔中,如何把消息從從XML文檔中提取出來。交換協議是我們本節討論的重點。

首先,我們開始手動搭建一個簡單的系統,在該系統中,XML請求和應答都通過HTTP協議傳輸,這將有助於我們從根本上理解消息交換傳輸的機制。然後,我們將使用REST來使我們的系統標準化。使用REST技術後的系統仍然是SOA的一個比較基本的實現,但系統將變得非常簡潔,並使用了良好設計的信息交換協議。最後,我們將採用SOAP技術讓這個系統成爲具有可移植性、更加完全和靈活的解決方案。

SOA設計之基礎---定義SOA服務的XML文檔

SOA服務的設計過程是一個會產生許多成果的過程。首先,它將產生一系列的服務定義列表,我們有時也稱之爲“服務目錄”,這些集合自然不是扁平的列表,而是以節或功能域的形式組織起來。比如,我們可能有商品功能域、訂單功能域及客戶功能域等等,在這些功能域下,我們可以定義一些具體的服務。例如在商品的功能域下,我們可以定義如下服務:

                insertItem---新增商品服務
                updateItem---
修改商品服務
                deleteItem---
刪除商品服務
                findItemById---
按照商品Id號發現商品的服務
                findAllItems---
找到所有商品的服務
                findItemsByCriteria---
按照某種規則找到商品的服務

再如,訂單功能域的服務可能有:

createOrder

findOrderById

findAllOrdersByCustomer

我們通常還需要定義一些跨功能域的服務(正交服務)。有些服務可以共享一些諸於控制流或交易處理之類的通用過程。

一般說來,在軟件設計的初始階段,人們都專注於基本的業務域對象及對這些業務對象進行處理的基本操作上,這些操作包括增刪改查等,亦稱之爲CRUD操作,即Create InsertReadselectUpdateDelete。不管您使用什麼語言,採用何種軟件架構,您的項目或產品都需要處理這些增刪改查操作。在我們這個例子中,基本業務域對象爲商品實體,上面服務列表中的前4個定義即爲該對實體的CRUD操作。

接下來,我們開始就商品這個業務對象的服務進行分析和設計。比如,insertItem服務可能具有如下格式:

創建

服務輸入

服務名

服務輸出

<Item>

<id>0</id> <code>RX004</code> <description>

Eth. Cable

</description>

</Item>

=> insertItem =>

<Result>

<retCode>

 OK

</retCode>

<id>137</id>

</Result>

 

客戶端如果想使用這個服務插入一個新的商品,它必須按照上面的輸入格式提供一個XML消息,請注意在輸入消息中,id屬性值爲0,這是因爲我們假定Server會在XML應答中爲這個商品分配一個id號。

對商品這個業務對象,其它的三個CRUD服務的XML定義可能如下:

讀取

服務輸入

服務名

服務輸出

<ItemId>
    
<id>137</id>
</ItemId>

=> findItemById =>

<Item>

<id>137</id>

<code>

RX004

</code>

<description>

 Eth. Cable 4 ft.

</description>

</Item>

 

修改

服務輸入

服務名

服務輸出

<ItemId>
      
<id>137</id>
</ItemId>

=> findItemById =>

<Item>

 <id>137</id>

<code> RX004

</code>

<description>

 Eth. Cable 4 ft.

</description>

</Item>

 

刪除

服務輸入

服務名

服務輸出

<ItemId>
 <id>137</id>
</ItemId>

=> deleteItem =>

<Result>
<retCode>

OK

</retCode>
<id>0</id>
</Result>

 

上面的設計就是一種我們可以採用的信息交換的例子。實際上,在上面的情況中,我們可以隨便決定使用何種信息交換協議,因爲它沒有任何約束。例如,我們可能發現,採用單一的輸入輸出模式可能更好,我們就可以把所有的CRUD操作都包含在一個服務中。那麼,此時的輸入XML消息中就會含服務名稱和服務對象,輸出的XML中會包括返回值和商品對象,它們的定義如下圖所示:

通用的CRUD操作

服務輸入

服務名

服務輸出

<ItemAction>

<method>

findById

</method>

<item>

<id>137</id>

<code></code>

<description> </description>

</item>

</ItemAction>

=> itemCrudService =>

<ItemActionResponse>

<retCode>OK</retCode> <item>

<id>137</id> <code>RX004</code> <description>

 Eth. Cable 4 ft.

</description>

</item>

</ItemActionResponse>

 

這裏我們只對CRUD操作定義了一個服務,但這是要付出代價的。當我們調用findById(通過id查找)delete(刪除)服務時,我們必須要爲輸入XML中提供商品的部分屬性值,其實,在這兩種情況下,id屬性值就足夠。只有在更新商品時,需要提供商品這個實體的所有屬性值。另外,在服務輸出的XML中,只有findById服務需要所有屬性都被賦值的商品對象。

但是,在通常情況下,增刪改查(CRUD)這四個服務是遠遠不夠的。例如,對商品這個業務對象而言,我們需要定義一個返回所有商品的方法,或者至少能返回一部分商品的方法。我們可能爲這個服務定義如下的規範:

非增刪改查(CRUD)操作

服務輸入

服務名

服務輸出

 

 

 

void input

 

 

 

=> findAllItems =>

<Items>

<item>

<id>137</id>     <code>RX004</code> <description>

Eth. Cable 4 ft.

</description>

</item>

...

</Items>

 

現在,我們已經討論了服務設計者在設計時可能採用的方法。您會注意到,消息交換的定義通常完全取決於您服務設計者,沒有什麼可以遵循的規則,您只需要使用您的設計技巧來抽象化這些概念。

一旦您完成服務的消息交換定義(您的定義可能如上所示,也有可能和上面的有所不同),我們就需要考慮網絡交換協議及其細節了。HTTP協議是一個實用而具有靈活性的協議:我們可以使用HTTP協議傳輸XML請求,這種方法也稱之爲POX-over-HTTP,這裏POXPlain OId XML的縮寫(簡單的舊XML格式)

在實踐中,我們只需要使用XML轉換API庫把我們程序語言中的對象轉換爲XML文檔,然後再轉化回對象即可,這樣,我們就可以實現我們上面所定義的服務。我們甚至還可以使用不同的語言實現客戶端和服務器端,知道它們都遵循我們上面的定義的消息交換規範。XML文檔在客戶服務器解耦過程中起了非常關鍵的作用,具體如下圖所示:

Web服務的解耦過程

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章