天天看點

《WCF技術内幕》翻譯10:第1部分_第2章_面向服務:填寫消息位址

填寫消息位址

現在我們已經看過了與消息互動的實體,詳細剖析了消息結構,然後看了一下WCF提供了幾個消息編碼器,現在我們來看一下如何在詳細發送的時候表示我們要發送的目的地。畢竟,除非能發送給接受者,否則消息等于是毫無用處。和郵政服務需要一個良好格式的位址結構一樣,面向服務的消息同樣也需要一個定義良好的位址結構。這節裡,我們将會建立自己的位址結構(Scheme),看它可以不可以廣泛适用于所有的消息應用系統,然後把它關聯到那個和WCF消息一起使用的位址結構上。

面向服務的消息直接在消息裡指定最終接受者。這是一個微不足道但是非常重要的問題。如果消息目标在消息裡指定,一個完整的消息模式集合成為可能。你将會在第3章《消息交換模式、拓撲和編排》裡學習到關于消息模式的更多知識。

當我們直接把位址插入到消息時,我們在為更高效的消息處理做準備。效率可以指代許多東西,在這個意義上,與建立消息的速度相反,我正在講的是實作一個更先進的消息發送行為的容易性。和寫個信封位址需要花費時間一樣,序列化位址到消息裡也需要耗費時間。無論如何,寫位址到信封上會改善郵政服務的效率,序列化位址到消息裡改善處理效率,特别當更進階的消息發送行為要實作(像消息路由和中介者)。

那什麼類型條目應該放到位址裡呢?對于發起者,位址應該可以辨別消息的最終接受者。因為最終接受者可能托管多個服務,是以我們應該有一個方式可以唯一地差別最終接受者上的服務。一個位址元素也能夠描述托管服務的最終接受者和服務本身。看一下下面的例子:

Internet時期,我們知道,位址由最終接受者的位址和我們可以使用的通路協定組成。因為大SO消息是SOAP消息,我們需要構造一些SOAP消息來傳達資訊。

我們知道SOAP消息包含3種類型的元素:信封、包含多個消息塊的消息頭和消息體。消息信封不是一個好的選擇,因為信封元素隻能出現一次。選擇隻有消息頭和消息體可以作為候選了。是以消息體怎麼樣?從我們早期的讨論,我們知道消息體隻是給最終的消息接受者使用的。經過排除過程以後,消息頭裡可以給我們提供包含位址的邏輯空間。這樣消息頭塊看起來會是什麼樣子?這樣如何:

<Envelope>    

<Header>        <To>http://wintellect.com/OrderService</To>    

</Header>    

<Body> …</Body>

</Envelope>

更高層次上,這個簡單的XML結構達到了我們辨別最終接受者和我們要發送的服務的目的。

在消息裡加入發送者資訊也許有用,就像信件上的退信位址。增加發送者資訊有2個目的:為最終接受者指出發送者,為中介者指出發送者。我們已經看到URL可以用來鑒别消息。是以事實上我們可以使用相同的結構來辨別發送者。例子如下:

<Envelope> 

<Header> 

        <To>http://wintellect.com/ReceiveService</To> 

        <From>http://wintellect.com/SendService</From> 

</Header> 

<Body> …</Body> 

給SOAP增加簡單的元素來支援消息的來源,這個可以既可以被中介者也可以被最終接受者使用。

如有出了問題會如何處理消息?每個現代的計算平台都有一些方法去訓示錯誤和異常資訊。這些消息處理機制使得我們的系統更加強壯、可預測和容易調試。很自然就想到我們的消息應用系統也應該有這樣的機制。考慮到我們的消息已經包含了<To> 和 <From>,我們可以發送所有的錯誤通知給<From>元素定義的位址。為了錯誤處理,如果我們想讓錯誤通知發送到一個特定的位址特别需要儲存什麼呢?這個例子,我們必須建立另外一個元素:

<Envelope> <Header>        <To>http://wintellect.com/OrderService</To>        <From>http://wintellect.com/SendService</From>        <Error>http://wintellect.com/ErrorService</Error> </Header> <Body> …</Body></Envelope>

增加<Error>元素到消息頭裡可以清楚地指出消息發送希望錯誤發送的位址。因為URL在消息頭裡,它可以被消息接受者和中介者使用。

我們簡單的位址結構需要發送者在消息裡增加我們的To、From和Error資訊作為消息頭塊,然後發送消息給最終接受者。當中介者或者最終接受者處理消息的時候,可能會發生錯誤。這個錯誤與初始發送的消息完全不同。從初始發送者的角度看,接受錯誤消息意味着自己的問題來了,但是特别是我們不知道的發送消息導緻的錯誤的時候。如果我們能把初始消息和錯誤消息關聯,對于調試、故障分析和審計來說就太棒了。為了實作這個,在消息裡我們需要兩個元素:消息辨別元素和消息關聯元素。讓先我們看一下消息辨別:

        <MessageID></MessageID> 

        <To>http://wintellect.com/OrderService</To> 

        <Error>http://wintellect.com/ErrorService</Error> 

<Body></Body> 

這個例子裡,我們把消息辨別元素稱作<MessageID>。現在,我們可以認為MessageID的值是一個全球唯一的數(例如GUID)。在産生上,這個數字對于其他參與者沒有任何意義。如果初始發送者産生了一個如前面描述的消息,所有的中介者和最終接受者都知道錯誤消息發送到哪裡,但是它們可以使用MessageID去找到導緻這個錯誤的特定消息。如果錯誤消息的接受者和初始發送者不同,這些處理必須在它們之間交換資訊區完全了解導緻錯誤的消息。

如果我們假設一個中介者或者最終接受者處理消息的時候産生了錯誤,那麼接下來就應該有一個新的消息被發送給錯誤元素指定的位址。如果一個中介者或者最終接受者發送了一個完全的新的消息,中介者或者最終接受者就會成為新消息的發送者。同樣地,錯誤消息頭塊裡的位址下載下傳成為了新消息的最終接受者。我們剛确定了導緻錯誤的初始消息會包含一個MessageID元素。不知道為什麼,錯誤消息需要包含一個引用MessageID的元素。初始消息和錯誤消息的關聯可以通過RelatesTo元素描述:

        <RelatesTo></RelatesTo> 

        <To>http://www.wintelelct.com/ErrorService</To> 

        <From>http://wintellect.com/OrderService</From> 

讓我們先不讨論錯誤消息,回到初始消息。正如你看到的,我們有一個方式去指定消息的最終接受者、初始發送者的位址、一個唯一辨別和錯誤通知發送的位址。同樣當處理初始消息的時候我們也想增加一個傳回消息的位址也是可行的。現實世界裡這種例子很多。比如,發貨單都有一個“回信到此”位址來區分初始的發送位址。我們的SO消息需要一個類似的結構。我們能多使用一次位址的概念與一個新的元素綁定來描述這個資訊。在下面的例子裡我們稱這個新的元素為ReplyTo:

        <ReplyTo>http://wintellect.com/OrderReplyService</ReplyTo> 

</Envelope> 

或許在相同的消息裡有From 和ReplyTo兩個元素,看起來有點重複。重要的是記住,From 和ReplyTo兩個元素或許表示相同的服務,但是它們也可以描述不同的服務。增加ReplyTo元素能夠簡單地為我們建立的消息頭塊增加更多的靈活性和功能。

下一個消息頭塊需要一些基礎,特别是你沒有太多處理Web服務的經驗。

Contoso Boomerang Corporation ATTN: New Customer Subscriptions 2611 Boomerang Way Atlanta, GA 30309

經驗來看,我們知道這個位址提到了Contoso Boomerang Corporation(Contoso回飛棒公司)。更準确點,我們知道這個位址在Contoso Boomerang Corporation組裡特别提到了New Customer Subscriptions

如果你希望發送郵件給一個大公司,你也許不需要特地指定一個部門。你可以發送郵件給Contoso Boomerang Corporation并且希望一些人打開這個郵件,猜一下誰會打開這個郵件,然後轉發給推斷的接受者。明顯這個過程和我們指定準确的部門或者小組位址相比将花費更多的時間。

Contoso Boomerang Corporation可能包括幾個可以收到這個信件的組。每個組或許有自己的一套處理流程。比如,Contoso或許有一個組負責簽訂新客戶,另外一個組負責客戶支援,再有另外一個組服務新産品開發。在抽象層上,位址可以為目标指定不懂級别的粒度,每個目标或許有自己的一套處理流程。

目前為止,我們已經建立了一個元素來定義最終接受者,一個reply-to接受者,一個錯誤提醒的接受者,一個消息辨別,一個關聯機制,初始發送者。我們還沒有定義一個消息的操作。我們假設,現在我們可以使用另外一個URL來辨別一個動作(action)或者操作(operation)。下面的例子闡述了這個假設:

        <Action>urn:ProcessOrder/Action> 

在這個例子裡,Action元素表示ProcessMsg操作應該在此消息上執行。OrderService也可能定義了另外的操作。例如,通過下面的Action元素我們可以發送另外一個消息去存檔消息操作:

        <Action>urn:ArchiveMessage</Action> 

        <ReplyTo>http://someotherurl.com/OrderReplyService</ReplyTo> 

<Body>    </Body> 

标準消息頭塊的需求

我們已經粗略地定義了7個可以幫助我們标記位址的元素。我們可以假設我們的元素名稱可以被普遍接受。我們能建立了解這些元素的基礎架構并且在我們每個消息參與者裡使用這個基礎架構。換句換說,我們不能發送這些消息到一個不知道我們7個元素意義的應用系統裡。同樣地,我們的應用系統不能夠接受包含不同位址頭的消息。例如,另外一個廠商或許已經定義了下面這樣的消息頭;

<Envelope> <Header>        <MessageIdentifier>1</MessageIdentifier>        <SendTo>http://wintellect.com/OrderService</SendTo>        <Op>http://wintellect.com/OrderService/ArchiveMessage</Op>        <Reply>http://someotherurl.com/OrderReplyService</Reply>        <SentFrom>http://wintellect.com/SendService</SentFrom>        <OnError>http://wintellect.com/ErrorService</OnError> </Header> <Body></Body></Envelope>

包含我們的基礎架構的應用系統不能夠處理這個消息。

如果我們打算調查一下大部分企業應用,我們會看到軟體廠商已經遵守這個模型定義了他們自己消息。過了幾年,SOAP已經成為大家認可的消息格式,但是還沒有就消息頭塊出現在消息裡達成共識,結果,應用系統部能輕易地互操作。真SOAP消息的互操作性需要一個可以通行于各個廠商的消息頭塊。如第一章裡提到的,WS-*規範通過定義一個公共的消息頭經過漫長的努力解決這個問題。

 本文轉自 frankxulei 51CTO部落格,原文連結:http://blog.51cto.com/frankxulei/318623,如需轉載請自行聯系原作者

繼續閱讀