使命必達: 深入剖析WCF的可靠會話[原理揭秘篇](上)
本係列先後通過《實例篇》、《概念篇》、《協議篇》和《編程篇》對WCF的可靠會話進行了詳細探討。作為本係列的最後一片,我們將深入到WCF的可靠會話體係的最底層,對實現可靠會話的實現原理進行深入剖析。如果讀者仔細閱讀本係列博文,相信會使讀者對可靠會話的理解提升到一定的高度。
從《編程篇》中,我們不難看出可靠會話的編程僅僅圍繞著一個對象,那就是綁定。綁定在整個WCF架構模型具有重要的地位。WCF整個架構模型由兩部分構成,即服務模型(Service Model)層和信道(Channel)層,而綁定是信道層的締造者,同時也是連接兩個層次的紐帶。對可靠會話的實現,是完全在信道層實現的。
綁定是由一係列綁定元素的有序組合,不同的保定元素具有各自的目的,而實現可靠會話的是一個叫做ReliableSessionBindingElement的綁定元素。在《WCF技術剖析(卷1)》的第3章對綁定模型的介紹中我們知道,綁定元素的主要任務是用於對信道管理器(Channel Manager)的創建。具體來說,在客戶端和服務端分別創建信道工廠(Channel Factory)和信道監聽器(Channel Listener)。由所有信道工廠和信道監聽器創建的信道按照其創建者的順序構建起一個消息處理的通道。
WCF通過最終通過ReliableSessionBindingElement創建可靠會話信道(Reliable Session Channel,以下簡稱RS信道),提供對可靠消息傳輸的實現。在WS-RM定義的可靠消息傳輸模型中,可靠消息傳輸是在RM源和RM目的地之間進行的,在這裏,你可以將客戶端和服務端的RS信道看成是RM源和RM目的地。
一、可靠會話信道層模型
圖1反映的是可靠會話在信道層的實現模型,從中我們可以看出可靠會話建立在客戶端和服務端的RS信道之間。作為客戶端或者服務端信道棧中的一員,RS信道在信道棧中位置由ReliableSessionBindingElement在綁定元素集合中的位置決定。由於RS信道作為RM源和RM目的地存在,所以WCF中的可靠消息傳輸保障存在於客戶端和服務端的RS信道之間。這其中不僅僅包括村存在於客戶端和服務端之間的傳輸網絡,也包括存在可靠會話信道之下的所有信道。相信大家還記得《實例篇》中的那個實例演示,我們用於模擬不穩定網絡環境的信道對應的綁定元素最終配置在可靠會話綁定元素之後。如果將其移到可靠會話綁定元素之前,由該自定義信道導致的消息丟失的問題將不能通過可靠會話解決。
圖1 可靠會話信道層模型
WS-RM為可靠消息傳輸的體現定義了一個可擴展的消息交換模型,而WCF的可靠會話時對該模型具體的實現。接下來,我們看看WCF的可靠會話是如何實現定義在WS-RM中的每一個消息交換步驟的。WCF目前支持WS-RM 1.0和1.1兩個版本,在這裏我們基於的是WS-RM 1.1。
二、CreateSequence 和CreateSequenceReponse
基於WS-RM的可靠消息傳輸是在一個RM序列中進行的,RM序列為實現可靠消息傳輸提供了一個上下文環境。對於WCF來說,與RM序列對應的概念就是可靠會話。基於WS-RM可靠消息傳輸從RM序列的創建開始,對於WCF來說,實現可靠消息傳輸需要首先創建可靠會話。WCF的可靠會話創建於可靠信道開啟之時。站在編程人員的角度,當服務代理對象開啟的時候,信道被打開。編程人員可以調用ICommunicationObject接口的Open方法顯式地開啟服務代理。如果服務代理在沒有被顯式開始的情況下被用於進行服務調用,WCF會對其進行隱式開啟。
WS-RM中序列創建過程從RM源向RM目的地發送主體包含CreateSequence元素的消息(以下簡稱CreateSequence消息)開始,到接收到對方返回的主體包含CreateSequenceResponse元素的回複消息(以下簡稱CreateSequenceResponse消息)作為結束。對於WCF可靠會話來說,客戶端和服務端信道棧中的RS信道充當著RM源和RM目的地的角色。當客戶端RS信道開啟的時候,它會創建CreateSequence消息,並沿著信道棧路徑發送到服務端。該CreateSequence消息被服務端信道棧接收並最終遞交給RS信道後,RS負責創建RM序列。序列創建成功後,可靠會話上下文在服務端部分被成功創建起來,被創建的RM序列被封裝到CreateSequenceResponse消息中返回到客戶端。當客戶端RS信道接收到CreateSequenceResponse消息後,創建可靠會話上下文在客戶端部分,至此,以兩個RS信道的客戶端會話被創建出來。
WS-RM中某個RM序列隻能保證單向的消息傳輸的可靠性,也就是說,確保從終結點A到B的可靠消息傳輸的RM序列不能提供從終結點B到A的可靠消息傳輸保障。要想解決這種雙向(Two-Way)可靠消息傳輸,需要借助於兩個RM序列。所以,對於非單向(One-Way)消息交換模式,請求|回複模式和雙工模式下的可靠消息傳輸需要雙RM序列的支持。WS-RM通過序列提供(Sequence Offering)的機製對此提供支持。接下來,我們來討論WCF的可靠會話對WS-RM序列提供機製的實現。
在客戶端RS信道開啟時,RS信道會先檢測當前終結點服務契約中所有服務操作采用的消息交換模式。如果所有操作均采用單向消息交換模式(通過應用在操作方法上的OperationContractAttribute的IsOneway屬性判斷),RS將不會采用序列提供機製。表現在消息交換上麵,就以為著CreateSequence消息的不會包含Offer元素。反之,如果服務契約包含任何一個非單向操作,RS信道會在客戶端創建入棧序列(Inbound Sequence),並將其作為提供序列封裝在CreateSequence消息的Offer元素中。下麵的XML片斷就是這樣一個包含提供序列的CreateSequence消息。需要注意的是,在RS信道生成的CreateSequence消息中,Offer/Endpoint和AcksTo的終結點引用是相同的。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <a:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequence</a:Action>
4: <a:MessageID>urn:uuid:f41d4443-fa5c-4f9d-95ff-96159c96ebec</a:MessageID>
5: <a:To s:mustUnderstand="1">https://www.artech.com/calculatorservice</a:To>
6: </s:Header>
7: <s:Body>
8: <CreateSequence xmlns="https://docs.oasis-open.org/ws-rx/wsrm/200702">
9: <AcksTo>
10: <a:Address>https://www.w3.org/2005/08/addressing/anonymous</a:Address>
11: </AcksTo>
12: <Offer>
13: <Identifier>urn:uuid:25b6383f-b5a1-4839-8249-b0f273a7f502</Identifier>
14: <Endpoint>
15: <a:Address>https://www.w3.org/2005/08/addressing/anonymous</a:Address>
16: </Endpoint>
17: <IncompleteSequenceBehavior>DiscardFollowingFirstGap</IncompleteSequenceBehavior>
18: </Offer>
19: </CreateSequence>
20: </s:Body>
21: </s:Envelope>
當包含Offer元素的CreateSequence消息被服務端RS信道成功接收到之後,會分析該元素封裝的客戶端提供的RM序列,如果滿足要求的話,它會選擇“接受”該序列。當客戶端提供的序列的接受,體現在服務端RS信道會在CreateSequenceResponse消息中添加一個Accept元素,下麵的XML就是這樣一個CreateSequenceResponse消息。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <a:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequenceResponse</a:Action>
4: <a:RelatesTo>urn:uuid:f41d4443-fa5c-4f9d-95ff-96159c96ebec</a:RelatesTo>
5: </s:Header>
6: <s:Body>
7: <CreateSequenceResponse xmlns="https://docs.oasis-open.org/ws-rx/wsrm/200702">
8: <Identifier>urn:uuid:97182c7f-ca5a-4c5f-8e17-6509387fa8bf</Identifier>
9: <IncompleteSequenceBehavior>DiscardFollowingFirstGap</IncompleteSequenceBehavior>
10: <Accept>
11: <AcksTo>
12: <a:Address>https://www.artech.com/calculatorservice</a:Address>
13: </AcksTo>
14: </Accept>
15: </CreateSequenceResponse>
16: </s:Body>
17: </s:Envelope>
三、 Sequence和SequenceAcknowledgement
當RM會話成功建立起來之後,相同於在客戶端和服務端的RS信道之間建立起了一個(對於所有操作均是單向的情況)或者兩個(操作列表中具有至少一個非單向的操作)RM序列。此後,當應用級別的消息通過傳入發送端(可能是客戶端,也可能是服務端)信道棧抵達RS信道的時候,RS信道會為之添加一個基於WS-RM的Sequence報頭。
同ASP .NET的會話一樣,WCF中的可靠會話實際上也可以看成是一種狀態保持機製,它將客戶端的服務調用請求關聯到RM序列這樣一個上下文中。可靠會話不但保持著創建的RM序列的標識,還保持一個計數器保存在會話生命周期內發送出的消息數量,該消息數量也就是消息的序號。
在發送端RS信道添加的Sequence報頭中,按照WS-RM的規定包含RM序列的標識和消息的序號。下麵的XML片斷為你展示的就是我們熟悉的計算服務調用請求經過客戶端RS信道後,整個請求消息的結構。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:r="https://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <r:Sequence s:mustUnderstand="1">
4: <r:Identifier>urn:uuid:2052a548-1505-4635-8995-a7c7e1e47379</r:Identifier>
5: <r:MessageNumber>1</r:MessageNumber>
6: </r:Sequence>
7: <a:Action s:mustUnderstand="1">https://www.artech.com/ICalculator/Add</a:Action>
8: <a:MessageID>urn:uuid:eacdc55d-eabf-4066-a958-5cc6753f1fe0</a:MessageID>
9: <a:ReplyTo>
10: <a:Address>https://www.w3.org/2005/08/addressing/anonymous</a:Address>
11: </a:ReplyTo>
12: <a:To s:mustUnderstand="1">https://www.artech.com/calculatorservice</a:To>
13: </s:Header>
14: <s:Body>
15: <Add xmlns="https://www.artech.com/">
16: <x>1</x>
17: <y>2</y>
18: </Add>
19: </s:Body>
20: </s:Envelope>
當包含Sequence報頭的消息被接收端信道棧的接收並將其遞交給RS信道時,RS信道負責對接收到的消息進行確認。WCF可靠會話采用兩種不同的確認機製,我個人將這兩種確認機製命名為“單獨確認”和“背負(Piggy-Back)確認”。對於前者,接收端RS信道會和創建一個空的消息,並添加相應的確認報頭。而對於後者,添加的確認報頭直接將其放置到另一個消息中,這個消息可以是應用相關,也可以是應用無關(比如關閉、終止序列的消息),甚至可以是錯誤(Fault)消息。無論采用怎樣的確認機製,接收端RS信道會在確認消息中添加SequenceAcknowledgement報頭,並指定RM序列標識和確認消息序號範圍。
一般來說,對於單向服務操作調用請求或者回調采用單獨確認機製,而對於基於請求/回複模式的服務調用或者回調,會采用背負確認機製。單獨確認機製機製和簡單,在這裏我們主要來談談背負確認。以請求|回複為例,假設在可靠會話情況下客戶端通過如下的代碼進行兩次服務調用。
1: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
2: {
3: ICalculator proxy = channelFactory.CreateChannel();
4: (proxy as ICommunicationObject).Open();
5: proxy.Add(1, 2);
6: proxy.Add(1, 2);
7: (proxy as ICommunicationObject).Close();
8: }
當第一次調用Add方法的時候,服務端會接收到如上麵XML所示的包含有Sequence報頭的請求消息。當服務端RS接收到該請求時,並不會立即對其進行確認,而是利用回複消息進行確認。具體地說,當請求消息被分發給服務模型層並成功執行後,執行後的結果被封裝成回複消息。當回複消息被傳入信道層後會被RS信道接收,此時它會將SequenceAcknowledgement報頭添加到回複消息中。下麵的XML片斷展示了客戶端最終接收到的回複消息。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:r="https://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <r:Sequence s:mustUnderstand="1">
4: <r:Identifier>urn:uuid:aeb0849b-cca9-4fc5-bdf4-06b6d4f2109b</r:Identifier>
5: <r:MessageNumber>1</r:MessageNumber>
6: </r:Sequence>
7: <r:SequenceAcknowledgement>
8: <r:Identifier>urn:uuid:2052a548-1505-4635-8995-a7c7e1e47379</r:Identifier>
9: <r:AcknowledgementRange Lower="1" Upper="1"/>
10: <netrm:BufferRemaining xmlns:netrm="https://schemas.microsoft.com/ws/2006/05/rm">8</netrm:BufferRemaining>
11: </r:SequenceAcknowledgement>
12: <a:Action s:mustUnderstand="1">https://www.artech.com/ICalculator/AddResponse</a:Action>
13: <a:RelatesTo>urn:uuid:eacdc55d-eabf-4066-a958-5cc6753f1fe0</a:RelatesTo>
14: </s:Header>
15: <s:Body>
16: <AddResponse xmlns="https://www.artech.com/">
17: <AddResult>3</AddResult>
18: </AddResponse>
19: </s:Body>
20: </s:Envelope>
從上麵的XML我們看到,回複消息的SequenceAcknowledgement報頭正是對請求消息的確認,因為消息序號範圍是從1到1(Lower="1" Upper="1")。除了SequenceAcknowledgement報頭之外,回複消息同樣具有一個Sequence報頭。原因很簡單,可靠會話不僅僅保障請求消息的可靠傳輸,同樣需要對回複消息的可靠傳輸提供保障。在前麵已經討論過了,可靠會話通過對WS-RM序列提供機製的實現,幫助實現消息傳輸的雙向保障。回複消息的Sequence報頭包含客戶端提供的RM序列標識和消息在該序列中的序號。
那麼回複消息又是如何被確認的呢?答案是通過第二次服務調用的請求消息。具體來說,當通過相同的服務代理第二次調用Add方法的時候,客戶端RS信道會在請求消息上麵添加SequenceAcknowledgement報頭作為對上一次回複消息的接收確認。我想你會想象得到服務端最終接收到的請求消息具有怎樣的結構,下麵的XML就是這樣一個消息。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:r="https://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <r:SequenceAcknowledgement>
4: <r:Identifier>urn:uuid:dc416e9a-2646-44ef-a289-0c008a2a3ba0</r:Identifier>
5: <r:AcknowledgementRange Lower="1" Upper="1"/>
6: </r:SequenceAcknowledgement>
7: <r:Sequence s:mustUnderstand="1">
8: <r:Identifier>urn:uuid:fda625fa-9db4-46c4-9688-76ae3bc288ea</r:Identifier>
9: <r:MessageNumber>2</r:MessageNumber>
10: </r:Sequence>
11: <a:Action s:mustUnderstand="1">https://www.artech.com/ICalculator/Add</a:Action>
12: <a:MessageID>urn:uuid:e79bcca9-ccc8-4e63-b893-22fc5adeb6d3</a:MessageID>
13: <a:ReplyTo>
14: <a:Address>https://www.w3.org/2005/08/addressing/anonymous</a:Address>
15: </a:ReplyTo>
16: <a:To s:mustUnderstand="1">https://www.artech.com/calculatorservice</a:To>
17: </s:Header>
18: <s:Body>
19: <Add xmlns="https://www.artech.com/">
20: <x>1</x>
21: <y>2</y>
22: </Add>
23: </s:Body>
24: </s:Envelope>
和第一次服務調用的請求消息進行對比,第二次服務調用多了一個AcknowledgementRange報頭實現對上一次回複消息的確認。你也應該想到,第二次服務調用請求會在回複消息中被確認。但是,聰明的讀者應該會有另一個問題,最後一次服務調用的回複消息如何被確認呢?
在前麵給出的服務調用代碼中,在進行第二次服務調用之後服務代理就被關閉了。第二次服務調用的回複消息貌似沒有被確認的機會了。實則不然,當關閉服務代理的時候,客戶端RS信道會向服務端發送一個主體部分包含CloseSequence元素的消息(以下簡稱CloseSequence消息)請求對RM序列的關閉,而該CloseSequence消息包含SequenceAcknowledgement報頭實現對客戶端接收到的所有回複消息的確認。整個消息發送和確認的過程如圖2所示。
圖2 請求/回複模式下的消息確認實現
四、 CloseSequence和CloseSeqenceResponse
當基於某個服務代理的所有服務調用結束,客戶端程序應該關閉該代理。在開啟可靠會話的情況下,服務代理的關閉同時意味著對可靠會話的終止(Termination),反映在WS-RM上就是對RM序列的終止。在RM序列終止之前,還有一個額外的過程,即RM序列的關閉。
服務代理的關閉反映在WCF信道層上就是對信道棧的關閉。當客戶端RS信道被關閉時,它負責關閉可靠會話。具體來講,它會按照WS-RM規範創建主體部分包含CloseSequence元素的消息(以下簡稱CloseSequence消息)。不過在發送CloseSequence消息之前,RS信道會等待所有已發消息的確認均已成功接收。如果在可靠會話生命周期內有消息發送,CloseSequence消息中還會包含最後一個消息的序號。如果此時還有回複消息或者回調消息等待確認,CloseSequence消息還包含對回複消息或者回調消息進行確認的SequenceAcknowledgement報頭。在該SequenceAcknowledgement報頭中具有一個Final元素表明這個最後一個確認。下麵的XML片斷展示了客戶端RS信道生成的CloseSequence消息。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:r="https://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <r:SequenceAcknowledgement>
4: <r:Identifier>urn:uuid:d3e23ddf-7b6d-474f-9171-78bb76f4f977</r:Identifier>
5: <r:AcknowledgementRange Lower="1" Upper="2"/>
6: <r:Final/>
7: </r:SequenceAcknowledgement>
8: <a:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/CloseSequence</a:Action>
9: <a:MessageID>urn:uuid:fa99c2cf-4910-4598-a8ac-0e5b73787cc8</a:MessageID>
10: <a:To s:mustUnderstand="1">https://www.artech.com/calculatorservice</a:To>
11: </s:Header>
12: <s:Body>
13: <r:CloseSequence>
14: <r:Identifier>urn:uuid:04127209-14ce-4648-90a5-a8eca6006fd2</r:Identifier>
15: <r:LastMsgNumber>2</r:LastMsgNumber>
16: </r:CloseSequence>
17: </s:Body>
18: </s:Envelope>
有一點需要提醒讀者的是,WS-RM規定RM源和RM目的地具有可以在某個時刻向對方發送對現有RM序列關閉或者終止的請求。但是,WCF僅僅對基於RM源序列終止或者關閉請求提供支持,也就是隻有客戶端的RS信道才能主動請求終止目前的可靠會話。
CloseSequence消息被服務端的RS信道成功接收之後,它同樣按照WS-RM規範生成主體部分包含CloseSequenceReponse元素的消息(以下簡稱CloseSequenceReponse消息),並回複給客戶端。CloseSequenceReponse消息同樣包含一個SequenceAcknowledgement報頭提供對所有接收到的消息的確認。和CloseSequence消息一樣,該SequenceAcknowledgement報頭包含和一Final元素表示這是最後一個確認。下麵的XML片斷為你展示了服務端RS生成的CloseSequenceReponse消息。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:r="https://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <r:SequenceAcknowledgement>
4: <r:Identifier>urn:uuid:04127209-14ce-4648-90a5-a8eca6006fd2</r:Identifier>
5: <r:AcknowledgementRange Lower="1" Upper="2"/>
6: <r:Final/>
7: <netrm:BufferRemaining xmlns:netrm="https://schemas.microsoft.com/ws/2006/05/rm">8</netrm:BufferRemaining>
8: </r:SequenceAcknowledgement>
9: <a:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/CloseSequenceResponse</a:Action>
10: <a:RelatesTo>urn:uuid:fa99c2cf-4910-4598-a8ac-0e5b73787cc8</a:RelatesTo>
11: </s:Header>
12: <s:Body>
13: <r:CloseSequenceResponse>
14: <r:Identifier>urn:uuid:04127209-14ce-4648-90a5-a8eca6006fd2</r:Identifier>
15: </r:CloseSequenceResponse>
16: </s:Body>
17: </s:Envelope>
五、 TerminateSequence和TerminateSequenceReponse
當客戶端和服務端的RS信道完成了CloseSequence/CloseSequenceResponse握手之後,正式為可靠會話的終止展開新一輪的對話。可靠會話的終止從客戶端RS信道向對方發送RM序列終止請求開始。具體來講,客戶端RS信道按照WS-RM規範生成一個主體部分包含TerminateSequence元素的消息(以下簡稱TerminateSequence消息),並發送給服務端。一般來說,TerminateSequence消息也會包含於CloseSequence消息一致的SequenceAcknowledgement報頭。如果在可靠會話生命周期內有過消息發送,TerminateSequence消息中還應該包含最有一個發送消息的序號,並且該序號與CloseSequence消息中的一致。下麵是一個由WCF客戶端RS信道生成的TerminateSequence消息。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:r="https://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <r:SequenceAcknowledgement>
4: <r:Identifier>urn:uuid:d3e23ddf-7b6d-474f-9171-78bb76f4f977</r:Identifier>
5: <r:AcknowledgementRange Lower="1" Upper="2"/>
6: <r:Final/>
7: </r:SequenceAcknowledgement>
8: <a:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/TerminateSequence</a:Action>
9: <a:MessageID>urn:uuid:651a09f7-795a-4331-9faf-1856f0975b47</a:MessageID>
10: <a:To s:mustUnderstand="1">https://www.artech.com/calculatorservice</a:To>
11: </s:Header>
12: <s:Body>
13: <r:TerminateSequence>
14: <r:Identifier>urn:uuid:04127209-14ce-4648-90a5-a8eca6006fd2</r:Identifier>
15: <r:LastMsgNumber>2</r:LastMsgNumber>
16: </r:TerminateSequence>
17: </s:Body>
18: </s:Envelope>
當服務端RS信道接收到TerminateSequence消息之後,作為回複,它會按照WS-RM規範生成一個主體包含TerminateSequenceResponse元素的消息(以下簡稱TerminateSequenceResponse消息)並返回給客戶端。一般來說,TerminateSequenceResponse消息具有於CloseSequenceResponse消息一樣的SequenceAcknowledgement報頭。下麵就是一個通過服務端RS信道生成的TerminateSequenceResponse消息。
1: <s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:r="https://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="https://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <r:SequenceAcknowledgement>
4: <r:Identifier>urn:uuid:04127209-14ce-4648-90a5-a8eca6006fd2</r:Identifier>
5: <r:AcknowledgementRange Lower="1" Upper="2"/>
6: <r:Final/>
7: <netrm:BufferRemaining xmlns:netrm="https://schemas.microsoft.com/ws/2006/05/rm">8</netrm:BufferRemaining>
8: </r:SequenceAcknowledgement>
9: <a:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/TerminateSequenceResponse</a:Action>
10: <a:RelatesTo>urn:uuid:651a09f7-795a-4331-9faf-1856f0975b47</a:RelatesTo>
11: </s:Header>
12: <s:Body>
13: <r:TerminateSequenceResponse>
14: <r:Identifier>urn:uuid:04127209-14ce-4648-90a5-a8eca6006fd2</r:Identifier>
15: </r:TerminateSequenceResponse>
16: </s:Body>
17: </s:Envelope>
六、 可靠會話如何實現流控製(Flow Control)
以上的內容充分反映了WCF可靠會話對WS-RM可靠消息傳輸模型的實現,接下來我們談談一個在WS-RM可靠消息傳輸模型沒有提及的主題:流控製。也就是說,流控製是WCF基於WS-RM規範的一個擴展,它的實現從一個方麵反映所有WS-*規範一個普遍的特點:可擴展性。WS-*是一個建立在XML上的規範體係,每一個WS-*規範為實現相應的協議進行的消息交換以及消息本身定義了一套規範。這些規範不應該限製太死,而應當給予規範的實現者充分的自由度去擴展它實現一些額外的功能。
在前麵已經說過,消息緩衝是WS-RM實現可靠消息傳輸的主要機製。消息緩衝機製反映在WCF的可靠會話上,就是客戶端和服務端的RS信道各自擁有消息緩衝區,它們的大小即容納消息的數量可以獨立地進行配置。對於消息發送端來說,如果消息緩衝區已滿,RS信道就不能處理從上層信道傳來的消息,直到接收到某個已發消息的確認後對應的消息從緩衝區中移除;對於消息接收端來講,如果緩衝區已滿,RS信道則不能處理來此下層信道傳來的消息,直到緩存的消息被成功交付後從緩衝區移除。
由於客戶端和服務端RS信道維持的消息緩衝區是相互獨立的,如果發送端的消息緩衝區遠遠大於接收端消息緩衝區的大小,就會導致消息在接收端出現阻塞的現象。如果我們將消息的傳輸比喻成一條河流的話,上麵的場景就意味著河流的源頭水量很大,但是下流河道很窄不能充分容納從源頭流流入的水量,這就必然導致河水泛濫。為了解決這個問題,WCF的可靠會話采用了流控製的機製。
實際上,流控製機製從實現上非常簡單,我將其稱為“接收端接收容量通知機製”。表現在消息交換上麵就是消息的接收端在對接收消息進行確認的時候,會順帶將本地消息緩衝區還能容納的消息數量一並放在確認消息中。消息發送端在接收到消息確認後,提取該值並確定是否繼續發送消息。
在我們上麵給出SequenceAcknowledgement消息中,細心的讀者可能留意到了在SequenceAcknowledgement報頭中具有一個在WS-RM規範沒有提及的元素BufferRemaining。該元素攜帶的數字就是接收端消息緩衝區中還能繼續接納的消息數量,如下麵的XML所示。
1: <r:SequenceAcknowledgement>
2: <r:Identifier>urn:uuid:04127209-14ce-4648-90a5-a8eca6006fd2</r:Identifier>
3: <r:AcknowledgementRange Lower="1" Upper="2"/>
4: <r:Final/>
5: <netrm:BufferRemaining xmlns:netrm="https://schemas.microsoft.com/ws/2006/05/rm">8</netrm:BufferRemaining>
6: </r:SequenceAcknowledgement>
由於WCF的可靠會話完全是在信道層實現的,而信道層就是由一係列用於處理消息的信道組成,所有從消息在信道層的交換可以幫助我們很容易地從本質上把握可靠會話的實現。在目前所有關於WCF的著作中,沒有一本能夠站在如此低層次地對可靠消息的實現進行剖析。作為一本深入剖析WCF實現機製的文章,我們還此基礎上對其進行進一步的挖掘。在《下篇》中,我們從傳輸協議的角度對可靠會話的實現進行進一步的剖析,敬請期待。
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-27 14:34:43