《WCF技術內幕》翻譯14:第1部分_第3章_消息交換模式、拓撲與編排:消息交換模式(MEP)
第3章:消息交換模式、拓撲和編排
當設計消息應用系統的時候,有必要考慮一下消息是怎樣在發送者、中介者 和接受者(前面章節介紹了這些消息參與者)流轉的。系統中消息交換可能性的 波動的值可以被不同程度地詳細描述。這些不同級別的細節就是總所周知的消息 交換模式(MEPS)、消息拓撲和消息編排【老徐備注1】。當從總體來看時,這 三個級別的細節讓我們抽象地描述任何消息場景。本章會詳細剖析消息交換模式 (MEPS)、消息拓撲、消息編排以及它們在WCF應用系統中如何被用來提供高級 功能。
消息交換模式
我們描述消息交換最常用的細節粒度就是消息交換模式(MEP)。根據W3C草案 (http://www.w3.org/2002/ws/cg/2/07/meps.html),一個MEP是“一個描述在消 息參與者之間交換的消息的模板。”一個MEP被限制在一個發送者和接收者之間 的邏輯連接上,整個行業也已經接受了這個事實。因為MEPs是個有點抽象的概念 ,近距離看一些現實世界的例子對於我們弄清MEPs這個概念有幫助。讓我們來看 看下面我和朋友關於足球票通話的例子:
1.我拿起電話,打給Rusty。
2.Rusty拿起手機。
3.Rusty說:Hello。
4.我說:你看今天的比賽了嗎?
5.Rusty說:是的,踢得太爛了。真難相信我們沒有贏。
6.Rusty說:他們全部啞火了。
對話繼續…
1-3步可以認為是傳輸規范的事件(我呼Rusty,他接電話,准備通話)。第4 步,我以問題的形式發給Rusty一個消息,習慣告訴我他應該回答。第5步, Rusty發送給我一個消息作為問題的回應。第6步是非主動請求的消息,發送給我 ,我可以回應或者不回應。這個消息的關聯性是非常含蓄的,因為它是對話流的 一部分。如果沒有前後場景的關聯,我會不知道他在說什麼,這將會很糟糕。就 像是一個推銷的電話或者象棋比賽一樣。
在這個場景裡,Rusty和我都可以隨便說任何內容(就想步驟6裡),我們也 可以一個接一個不停地聊下去。一個單向的談話也有可能,就像股東電話會議, 或者因為一方終止了通話在回應發送以前,讓我們看一下另外一個電話通話的例 子:
1.Lewis(我老板)拿起電話,撥我的手機號。
2.我拿起手機說:“Hello”。
3.Lewis說:“你干的太好了。我會立即給你100%的加薪,”。
4.Lewis掛斷了電話。
5.我打回給Lewis。
6.Lewis接電話。
7.我說:“你太大方了,我只要50%就可以啦。”。
8.我掛斷電話。
9.Lewis又打過來。
10.我接聽電話
11.Lewis說:“100%加薪是最終結果,我現在正開一個藍色保時捷911 Turbo 趕過去,確保你能和我簽約。”
12.Lewis又掛了。
在前面的場景裡,我可以回應,但Lewis如此想給我加薪,以至於都沒聽我的 答復。我只要再打給他進一步討論詳情。概念上說,消息交換裡的響應需要發送 者偵聽在一個存在的連接或者一個新的連接上。
通話可以使用更嚴格的模型。想想一下飛行員和機場控制塔台之間的對話, 如果你曾經聽過這些通信,結構顯而易見:
1.控制台呼叫飛行員:“Contoso437,轉向180度,300哩(節/小時, 飛行速度),下降到10000英尺。”
2.飛行員回答:“Contoso437,正在轉向180度,300哩(節/小時,飛行速度) ,正要下降到10000英尺”。
這個場景裡,控制塔變化請求,要求回復。如果飛機沒有回復,羅嗦的控制 台會重復這個指令直到一個收到響應為止,或者采取其他的一些行動。進一步說 ,這個協議要求飛行員當控制塔正在通信的時候不能打斷塔台。
這些簡單的比喻對理解面向服務應用系統中的消息參與者之間交互很有幫助 。概括地說,MEPs 是根據參與者如何交互、更確切地說,允許回復的數量和發 送者和接受者之間需要不需要新的連接來分類的。也許電話和無線通話有所不同 ,但是在面向服務的世界裡通常有3種類型的MEPs:數據報、請求-應答和雙工。
數據報交換模式
圖3-1說明了數據報消息交換模式。也會被稱為simplex(單一模式),這個 MEP表示一個單向的消息發送,或者即發即棄的發送。使用這個MEP的消息發送被 稱為數據報MEP。概念上說,一個數據報與電話捉迷藏游戲裡的語音留言很相似 【老徐備注3】。當你正在語音留言的時候,你可能不想立即得到回復,你也許 希望以後回個電話。回復一個數據報消息被認為是帶外的(out of band)。換 句話說,就是需要發送者和接受者之間建立一個新的連接來響應數據報。
圖3-1數據報消息交換模式
數據報和WSDL
一個數據報MEP(消息交換模式)使用WSDL語言描述為一個包含wsdl:input 和 沒有wsdl:output元素的操作。例如,下面的WSDL代碼塊描述了一個可以接受輸 入的操作,因此是一個使用數據報MEP的操作。
<wsdl:portType name="ISomeContract" >
<wsdl:operation name="SomeOperation">
<wsdl:input wsa10:Action="urn:SomeActionInput" />
</wsdl:operation>
</wsdl:portType>
數據報和WCF契約
創建一個使用數據報MEP的WCF程序,是相當容易的。通常,我們從下面的契 約開始:
// File: Contract.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
[ServiceContract(Namespace="http://wintellect.com/SomeContract",
Name="ISomeContract")]
public interface ISomeContract {
[OperationContract(Name="SomeOperation",
Action="urn:SomeActionInput",
IsOneWay=true)]
void SomeOperation(Message message);
}
這個例子裡有2個重要的東西:SomeOperation方法的Void返回類型和 OperationContractAttribute屬性的的IsOneWay標記。
Void返回類型 因為我們正在使用C#接口去描述一個數據報消息操作,我們需 要去用一個返回類型來表示這是一個單向的操作。用來描述數據報消息操作的方 法必須有一個Void返回類型。指定別的返回類型,WCF運行時在驗證契約的時候 會拋出一個InvalidOperationException異常。
操作契約的IsOneWay屬性 當定義一個數據報操作的時候,只定義一個帶Void 返回類型的接口是不夠的。考慮下面的契約:
// File: Contract.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
[ServiceContract(Namespace="http://wintellect.com/SomeContract",
Name="ISomeContract")]
public interface ISomeContract {
[OperationContract(Name="SomeOperation",
Action="urn:SomeActionInput",
ReplyAction="urn:SomeActionOutput")]
void SomeOperation(Message message);
}
這個契約最後轉換的WSDL描述如下:
<wsdl:portType name="ISomeContract" >
<wsdl:operation name="SomeOperation">
<wsdl:input wsaw:Action="urn:SomeActionInput" />
<wsdl:output wsaw:Action="urn:SomeActionOutput"/>
</wsdl:operation>
</wsdl:portType>
wsdl:output元素的出現表示應該有個消息回復發送者。因此我們接口方法的 返回類型是無效的,消息會有一個空的消息體元素(<Body>)。盡管消息 體力沒有數據,仍然會有回復。重要的是要注意WCF運行時必須產生這個回復消 息,並且每次收到一個有效消息的時候都需要處理工作。在WCF裡,唯一避免回 復消息的方法就是在操作屬性裡設置IsOneWay屬性。默認的值是false,這個設 置使得操作默認使用請求/應答MEP(簡要討論一下)。
錯誤處理考慮
數據報MEP涉及到了一個有趣的面向服務的錯誤處理的手法。正如你將會再第 4 章:WCF101裡看到的一樣,錯誤可以被序列化為SOAP錯誤,並且這些錯誤可以 被發送到指定的終結點。使用數據報MEP,發送者沒有義務去偵聽這些錯誤消息。 如果發送者希望接受這些消息或者發送到其它的終結點,發送者必須在消息頭塊 裡的<FaultTo>指定地址。然後接受者會盡全力把錯誤消息發送給指定的 終結點。
HTTP和數據報MEP
所有的傳輸都支持數據報消息交換模式,但是一些傳輸,像HTTP和HTTPS, 已 經有相對應的機制內建到傳數裡。當消息通過這些傳輸協議傳輸的時候,發送者 希望接受一個響應,並且發送者希望發送一個響應。例如,當我們對一個頁面發 送 HTTP請求的時候,我們希望得到一個HTTP回復。同樣地,一個Web服務器希望 在收到一個資源請求以後能夠發送給客戶端一個HTTP響應。HTTP響應是通過反向 通道傳輸的。為了這個討論,我們可以把反向通道理解為一個偵聽器,它會再響 應消息發送以後停止偵聽。
在WCF裡,當我們通過HTTP發送一個數據報,我們通過HTTP發送數據,回復是 一個HTTP202響應代碼【原書備注1】。通常來說,當一個數據報消息通過一個要 求內建應答機制的傳輸來發送的時候,響應消息會包含一個傳輸規范規定的應答 ,而沒有消息規范信息。下面是一個通過HTTP發送的數據報的響應消息:
通過HTTP接收數據報的WCF應用,會再處理數據報以前發送一個202應答。這 個優化意味著客戶端不需要不必要地等待傳輸應答,並且這個交換方式非常接近 單向模式。
請求/響應MEP
廣義上說,Internet是建立在請求/響應消息交換模式上的(MEP,同樣被稱 作半雙工half-duplex)。我們希望一個單一的頁面請求會產生一個HTML響應。 如果我們希望看到另外一個頁面,我們要發送新的請求。換句話說,給我們請求 的響應是帶內方式的(in band,和上面的帶外out of band對應,這裡指的是使 用一個通道)。圖3-2概念上說明了請求/響應MEP。
圖3-2請求/響應MEP
備注:請求/響應消息交換模式隱藏的很深,所以當我們使用它的時候也很難 注意的到。大部分情況,關於請求/響應模式,我們的經驗會讓我們條件反射式 的思考。比如,我們的大部分基於組件的frameworks要求我們調用一個方法以後 要等待方法返回。許多分布式frameworks(像COM)更是強化了這一點,因為這 些frameworks允許我們去調用一個方法並且等待響應。我鼓勵你去從這個缺省的 MEP“自由思考”,強迫自己去思考一下WCF中可能存在的其它MEP。
請求/響應和WSDL
請求/響應消息交換模式使用WSDL表述為一個包含wsdl:input 和wsdl:output 元素的操作。例如,下面的WSDL代碼塊描述的就是一個請求/響應消息交換模式 :
<wsdl:portType name="ISomeContract" >
<wsdl:operation name="SomeOperation">
<wsdl:input wsaw:Action="urn:SomeActionInput" />
<wsdl:output wsaw:Action="urn:SomeActionOutput" />
</wsdl:operation>
</wsdl:portType>
重要的是要注意代碼裡的wsdl:input 和wsdl:output元素。這些元素的順序 暗示了在一個響應消息發送以前,請求消息必須先被接收。
Request/Reply and WCF Contracts
請求/響應和WCF契約
WCF操作契約缺省使用請求/響應MEP.任何WCF認為可序列化的類型都可以指定 為一個返回類型。(第9章:契約裡會討論數據和消息的序列化。)比如,下面 的契約使用請求/應答模式:
// File: Contract.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
[ServiceContract(Namespace="http://wintellect.com/SomeContract",
Name="ISomeContract")]
public interface ISomeContract2 {
[OperationContract(Name="SomeOperation",
Action="urn:SomeActionInput",
ReplyAction="urn:SomeActionOutput")]
Message SomeOperation(Message message);
}
傳輸考慮
一些傳輸,像用戶報文協議(UDP)和MSMQ,本質上都是單向的。當WCF最初 發布的時候,還沒有對於使用MSMQ的請求/應答MEP的直接支持,同樣也不支持 UDP。通過MSMQ傳輸的請求/應答MEP需要,一個像接受者和發送者之間的連接一 樣的連接。當然,一個自定義通道也是可以的。我們將會在第6章:通道,裡詳 細介紹。
向你在第2章:“面向服務”裡看到的一樣,有幾個可以定義接受者發送回復 或者錯誤消息的WS-Addressing消息頭塊。當使用像TCP、HTTP或者命名管道這樣 的傳輸時,接受者可以通過反向通道發送響應消息。在這些場景裡,WS- Addressing表示響應消息應該發送到<ReplyTo>消息頭塊定義的 http://www.w3.org/2005/08/addressing/anonymous地址。這些請求消息裡的內 容如下:
<s:Envelope >
<s:Header>
<a:Action s:mustUnderstand="1">urn:SomeActionRequest</a:Action>
<a:MessageID>urn:12345</a:MessageID>
- <a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a: Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">
net.tcp://localhost:8000/SomeOperation
</a:To>
</s:Header>
<s:Body></s:Body>
</s:Envelope>
響應消息如下:
<s:Envelope >
<s:Header>
<a:Action s:mustUnderstand="1">urn:SomeContractReply</a:Action>
<a:RelatesTo>urn:12345</a:RelatesTo>
<a:To s:mustUnderstand="1">
http://www.w3.org/2005/08/addressing/anonymous
</a:To>
</s:Header>
<s:Body></s:Body>
</s:Envelope>
雙工MEP
雙工是同時發送和接受消息,是一個我們在電話通話裡見到的交互方式。在 一個消息應用裡,雙工MEP定義了一個允許同時在接受者和發送者發送消息的操 作的集合,反之亦然。圖3-3說明了雙工MEP
圖3-3雙工MEP
雙工MEP和WSDL
在雙工消息交換模式裡發送者和接受者可以自由地發送消息。與雙工消息交 換模式相關的WSDL包含2個操作。一個操作(SomeOperation)表示消息發送給接 收者,另外一個操作(SomeCallbackOperation)表示消息從接受者回發給發送者 。
<wsdl:portType name="ISomeContract" >
<wsdl:operation name="SomeOperation">
<wsdl:input wsa10:Action="urn:SomeActionRequest" />
</wsdl:operation>
<wsdl:operation name="SomeCallbackOperation">
<wsdl:output wsa10:Action="urn:SomeCallbackRequest" />
</wsdl:operation>
</wsdl:portType>
某種意義上,雙工MEP是其它消息交換模式(MEP)的結合。例如,前面的 WSDL代碼描述了2個操作,換句話說,一個消息報可以從發送者發送給接受者, 反之亦然,消息也可以從接受者發送給發送者。也可能是消息在這些參與者通過 請求/應答MEP來發送。思考一下下面的代碼:
<wsdl:portType name="ISomeContract">
<wsdl:operation name="SomeOperation">
<wsdl:input wsaw:Action="urn:SomeActionRequest" />
<wsdl:output wsaw:Action="urn:SomeContractReply" />
</wsdl:operation>
<wsdl:operation name="SomeCallbackOperation">
<wsdl:output wsaw:Action="urn:SomeCallbackContractRequest" />
<wsdl:input wsaw:Action="urn:SomeCallbackContractReply" />
</wsdl:operation>
</wsdl:portType>
SomeOperation操作描述了從發送者到接收者(urn:SomeActionRequest)和 接收者回發給發送者(urn:SomeContractReply)的消息。 SomeCallbackOperation操作表示從接受者到客戶端 (urn:SomeCallbackContractRequest)和回發給接受者 (urn:SomeContractReply)的消息。
雙工MEP和WCF契約
WCF契約創建雙工MEP在語法上咋一看有點奇怪。如前面所描述的,雙工通信 需要2個契約。習慣上,描述發送給接收應用程序的消息(和回復消息,如果有 的話)的契約被稱作服務契約,描述接收者發送給發送者的契約被稱作回調契約 。這2個契約被服務契約的ServiceContractAttribute.CallbackContract屬性關 聯。如下所示:
// File: Contract.cs
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
// the service contract looks the same as before, except
// for the addition of the CallbackContract property
// IsOneWay=true can also be set
[ServiceContract (Namespace="http://wintellect.com/SomeContract",
Name="ISomeContract",
CallbackContract=typeof (ICallbackContract))]
public interface ISomeContract3 {
[OperationContract(Name="SomeOperation",
Action="urn:SomeActionRequest",
ReplyAction="urn:SomeContractReply")]
void SomeOperation(Message message);
}
// No ServiceContract is necessary on the callback contract
// IsOneWay=true can also be set
public interface ICallbackContract {
[OperationContract(Name="SomeCallbackOperation",
Action="urn:SomeCallbackContractRequest",
ReplyAction="urn:SomeCallbackContractReply")]
void SomeCallbackOperation(Message message);
}
注意一下回調契約被服務契約的ServiceContractAttribute類的 CallbackContract屬性引用。
提醒:當創建一個雙工契約的時候,要記得標記操作為單向操作。如果 OperationContractAttribute的IsOneWay 屬性沒有標記,消息交換模式會采用 默認的請求/應答模式。2個參與者會為了創建回復消息而產生額外的開銷。設置 IsOneWay 屬性為True可以減少每次消息交互的開銷。
【原書備注】:
[1] HTTP202狀態代碼定義如下:請求已經被接受處理,但是處理還沒結束。 請求也許執行,也許不執行,因為處理發生的時候也請求許會被拒絕。發送一個 這樣的異步操作狀態代碼非常的麻煩。
202響應是非義務性的。它的目的是允許服務器接受其它進程一個請求(可能 是每天運行一次的批處理進程)而不需要用戶代理一直連接著服務等待處理結束 。返回的響應消息實體應該包含請求的當前狀態或者指向狀態監控器的一個指針 ,或者當用戶希望完成的請求的一些估計信息。
【老徐備注】:
1.MEPS:消息交換模式,WCF支持6中消息交換模式:
* 數據報
* 請求-響應
* 雙工
* 帶會話的數據報
* 帶會話的請求-響應
* 帶會話的雙工
參考http://msdn.microsoft.com/zh-cn/library/aa751829.aspx
2.Message Choreographies:消息編排,Web服務編制(Web Services Orchestration,WSO)指為業務流程(business processes)而進行Web服務合 成,而Web服務編排(Web Services Choreography,WSC)指為業務協作 (business collaborations)而進行Web服務合成。參考: http://bbs.w3china.org/dispbbs.asp?boardID=10&ID=34104。
3.互相給對方電話留言;玩電話追逐游戲.比如我打電話給一個朋友,他不在 ,我就留了言。他給我回電話的時候,我又不在,所以他留了言。 這樣互相留 言好幾次,誰也找不到對方, 真是像捉迷藏一樣。參考金山詞霸。