823
技術社區[雲棲]
使命必達: 深入剖析WCF的可靠會話[協議篇](下)
在《上篇》中,我們認識了從序列創建到終止過程中消息交換的大致流程。接下來,我們進一步將關注點聚焦到單個小消息上,看看在整個基於序列的上下文中,不同類型的消息具有怎樣的結構。首先從序列的創建開始。
一、CreateSequence 和CreateSequenceReponse
基於WS-RM的可靠消息傳輸從序列的創建開始。為了創建序列,RM源(RM Source)向RM目的地(RM Destination)發送一個主體包含CreateSequence元素的SOAP消息。CreateSequence元素攜帶序列相關的屬性,比如確認消息被發送的目的終結點引用,序列過期時限以及序列相關的其它信息。成功接收到序列創建請求後,RM目的地成功創建序列,並將序列相關信息封裝到CreateSequenceReponse元素中,並最終通過SOAP消息返回。
對於參與消息傳輸的某個結點來說,消息傳輸是具有方向的。通過上麵的方式創建的序列為從RM源到目的地的消息可靠傳輸提供了一個執行上下文,對於源來說,這是出棧可靠消息傳輸(Outbound RM),創建出來的序列被稱為出棧序列(Outbound Sequence)。但是,在非單向(One-way)消息交換模式下,我們同樣需要保障消息傳輸目的地到消息傳輸源之間消息傳輸的可靠性,即入棧可靠消息傳輸。在這種情況下,消息傳輸源會在本地創建入棧序列(Inbound Sequence),並將序列的內容嵌入到序列創建請求的CreateSequence元素中。RM目的地會接收到包含有入棧序列(針對於RM源來說)的序列創建請求消息後,會選擇接受或者拒絕源提供的序列。
1: <wsrm:CreateSequence ...>
2: <wsrm:AcksTo> wsa:EndpointReferenceType </wsrm:AcksTo>
3: <wsrm:Expires ...> xs:duration </wsrm:Expires> ?
4: <wsrm:Offer ...>
5: <wsrm:Identifier ...> xs:anyURI </wsrm:Identifier>
6: <wsrm:Endpoint> wsa:EndpointReferenceType </wsrm:Endpoint>
7: <wsrm:Expires ...> xs:duration </wsrm:Expires> ?
8: <wsrm:IncompleteSequenceBehavior>
9: wsrm:IncompleteSequenceBehaviorType
10: </wsrm:IncompleteSequenceBehavior> ?
11: ...
12: </wsrm:Offer> ?
13: ...
14: </wsrm:CreateSequence>
上麵的XML片斷展示了CreateSequence元素的結構。其中<wsrm:AcksTo>是必須的,它表示可靠消息目的地發送確認消息的目的終結點引用。而<wsrm:Expires>表示請求創建的可靠消息傳輸序列的過期時限。如果該值為PT0S,則表明可靠消息傳輸序列永不過期。如果沒有顯式指定,該值為PT0S。
WS-RM中某個RM序列隻能保證單向的消息傳輸的可靠性,也就是說,確保從終結點A到B的可靠消息傳輸的RM序列不能提供從終結點B到A的可靠消息傳輸保障。要想解決這種雙向(Two-Way)可靠消息傳輸,需要借助於兩個RM序列。所以,對於非單向(One-Way)消息交換模式,請求|回複模式和雙工模式下的可靠消息傳輸需要雙RM序列的支持。WS-RM通過序列提供(Sequence Offering)機製對此提供支持。如果兩個終結點之間存在請求|回複或者雙工消息交換模式,RM源會在本地創建RM序列,並將創建的序列封裝到CreateSequence/Offer元素中,提供給RM目的地。限於篇幅的局限,CreateSequence/Offer結點中每一個元素的含義在這裏就不單獨介紹了,對此有興趣的讀者可以參考WS-RM 1.1的官方文檔。下麵是一個典型的包含CreateSequence元素的WS-RM序列創建請求消息。
1: <s:Envelope>
2: <s:Header>
3: <wsa:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequence</wsa:Action>
4: <wsa:MessageID>urn:uuid:949cca61-8813-42ff-ab33-18d9e3fa82fa</wsa:MessageID>
5: <wsa:ReplyTo>
6: <wsa:Address>https://www.artech.com/client</wsa:Address>
7: </wsa:ReplyTo>
8: <wsa:To s:mustUnderstand="1">https://www.artech.com/service</wsa:To>
9: </s:Header>
10: <s:Body>
11: <wsrm:CreateSequence>
12: <wsrm:AcksTo>
13: <wsa:Address>https://www.artech.com/client</wsa:Address>
14: </wsrm:AcksTo>
15: <wsrm:Offer> <wsrm:Identifier>urn:uuid:066b4730-fc82-458a-a5c1-210be4fb4e4e</wsrm:Identifier>
16: <wsrm:Endpoint>
17: <wsa:Address> https://www.artech.com/client</wsa:Address>
18: </wsrm:Endpoint> <wsrm:IncompleteSequenceBehavior>DiscardFollowingFirstGap</wsrm:IncompleteSequenceBehavior>
19: </wsrm:Offer>
20: </wsrm:CreateSequence>
21: </s:Body>
22: </s:Envelope>
RM目的地接收到序列創建請求的CreateSequence消息後,會創建序列,並將序列的相關信息封裝到回複消息中。當RM源接收到了回複之後,意味這它可以在指定的序列(通過序列的標識)上下文中發送消息了。可靠消息傳輸回複消息的主體部分包含一個CreateSequenceResponse元素,該元素具有如下的結構。
1: <wsrm:CreateSequenceResponse>
2: <wsrm:Identifier> xs:anyURI </wsrm:Identifier>
3: <wsrm:Expires> xs:duration </wsrm:Expires> ?
4: <wsrm:IncompleteSequenceBehavior>
5: wsrm:IncompleteSequenceBehaviorType
6: </wsrm:IncompleteSequenceBehavior> ?
7: <wsrm:Accept>
8: <wsrm:AcksTo> wsa:EndpointReferenceType </wsrm:AcksTo>
9: ...
10: </wsrm:Accept> ?
11: ...
12: </wsrm:CreateSequenceResponse>
其中<wsrm:Identifier>中的URI為創建序列的唯一標識。<wsrm:Expires>為序列過期時間,一般來說,本創建的序列過期時間小於或者等於CreateSequence請求中指定的RM源所期望的過期時間。如果該值為PT0S,意味著序列永不過期。當該元素缺省時,默認值為PT0S。<wsrm:Accept>元素封裝的部分作為對CreateSequence請求中提供的序列的接受。下麵的XML片斷展示一個典型的包含CreateSequenceResponse元素的SOAP消息。
1: <s:Envelope>
2: <s:Header>
3: <wsa:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequenceResponse</wsa:Action>
4: <wsa:RelatesTo>urn:uuid:949cca61-8813-42ff-ab33-18d9e3fa82fa</wsa:RelatesTo>
5: <wsa:To s:mustUnderstand="1">https://www.artech.com/client</wsa:To>
6: </s:Header>
7: <s:Body>
8: <wsrm:CreateSequenceResponse>
9: <wsrm:Identifier>urn:uuid:656652b8-9af2-4e94-9d07-2dc21c05ed27</wsrm:Identifier>
10: <wsrm:IncompleteSequenceBehavior>DiscardFollowingFirstGap</wsrm:IncompleteSequenceBehavior>
11: <wsrm:Accept>
12: <wsrm:AcksTo>
13: <wsa:Address>https://www.artech.com/service</wsa:Address>
14: </wsrm:AcksTo>
15: </wsrm:Accept>
16: </wsrm:CreateSequenceResponse>
17: </s:Body>
18: </s:Envelope>
二、CloseSequence和CloseSeqenceResponse
需要注意的是,這裏講的序列的關閉(Close),並不是指上麵消息交換流程中的序列的終止(Terminate),這是一個上麵沒有提及的概念。在可靠消息傳輸序列被使用過程中,RM源和RM目的地都有可以希望停止使用該序列。而對序列的終止會使RM源和目的地失去其現有的狀態,所以為了確保可靠消息序列能夠以一種可知的最終狀態結束,RM源和RM目的地均可以在最終終止序列之前選擇將其關閉。
如果一方(RM源或者RM目的地)希望關閉現有的序列,它會像另一個方法送序列關閉請求消息。該消息主體部分包含一個CloseSequence元素,該元素結構如下。
1: <wsrm:CloseSequence>
2: <wsrm:Identifier> xs:anyURI </wsrm:Identifier>
3: <wsrm:LastMsgNumber> wsrm:MessageNumberType </wsrm:LastMsgNumber> ?
4: ...
5: </wsrm:CloseSequence>
<wsrm:Identifier>的值為序列的唯一標識。<wsrm:LastMsgNumber>是最後發送的消息序號,也是在該序列上下文中發送的最後一個消息的序號。下麵是一個典型的包含有CloseSequence元素的SOAP消息。
1: <s:Envelope>
2: <s:Header>
3: <wsa:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/CloseSequence</wsa:Action>
4: <wsa:MessageID>urn:uuid:6ce1d4c3-e1c1-474f-a8c9-4210e37f7877</wsa:MessageID>
5: <wsa:ReplyTo>
6: <wsa:Address>https://www.artech.com/client</wsa:Address>
7: </wsa:ReplyTo>
8: <wsa:To s:mustUnderstand="1">https://www.artech.com/service</wsa:To>
9: </s:Header>
10: <s:Body>
11: <wsrm:CloseSequence>
12: <wsrm:Identifier>urn:uuid:656652b8-9af2-4e94-9d07-2dc21c05ed27</wsrm:Identifier>
13: <wsrm:LastMsgNumber>30</wsrm:LastMsgNumber>
14: </wsrm:CloseSequence>
15: </s:Body>
16: </s:Envelope>
當CloseSequence請求消息被另一方接收之後,它不運行再接收該序列範圍內的其它消息。作為回複,它會創建一個主體部分包含有CloseSequenceResponse元素的SOAP消息。CloseSequenceResponse元素的結構如下。
1: <wsrm:CloseSequenceResponse>
2: <wsrm:Identifier> xs:anyURI </wsrm:Identifier>
3: ...
4: </wsrm:CloseSequenceResponse>
在CloseSequenceResponse元素中,隻有包含有序列標識的<wsrm:Identifier>是必須的。除了主體部分包含CloseSeqenceResponse元素之外,序列關閉回複消息還必須包含SequenceAcknowledgement確認報頭,並且該報頭具有Final子元素。下麵是一個典型包含CloseSequenceResponse元素的SOAP消息。
1: <s:Envelope>
2: <s:Header>
3: <wsrm:SequenceAcknowledgement>
4: <wsrm:Identifier>urn:uuid:656652b8-9af2-4e94-9d07-2dc21c05ed27</wsrm:Identifier>
5: <wsrm:AcknowledgementRange Lower="1" Upper="30"></wsrm:AcknowledgementRange>
6: <wsrm:Final></wsrm:Final>
7: <netrm:BufferRemaining>8</netrm:BufferRemaining>
8: </wsrm:SequenceAcknowledgement>
9: <wsa:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/CloseSequenceResponse</wsa:Action>
10: <wsa:RelatesTo>urn:uuid:6ce1d4c3-e1c1-474f-a8c9-4210e37f7877</wsa:RelatesTo>
11: <wsa:To s:mustUnderstand="1">https://www.artech.com/client</wsa:To>
12: </s:Header>
13: <s:Body>
14: <wsrm:CloseSequenceResponse>
15: <wsrm:Identifier>urn:uuid:656652b8-9af2-4e94-9d07-2dc21c05ed27</wsrm:Identifier>
16: </wsrm:CloseSequenceResponse>
17: </s:Body>
18: </s:Envelope>
三、TerminateSequence和TerminateSequenceReponse
當RM源完成所有消息傳輸工作之後,不再需要當前的序列,它可以向RM目的地發送一個主體部分包含TerminateSequence元素的消息,通知對方當前序列已經完成,並且不會繼續發送基於該序列的消息。成功接受到序列終止請求消息後,RM目的地可以進行許當前序列相關的資源回收工作。在正常的情況下,RM源會在接收到所有消息的確認後才會向RM目的地發送序列終止的請求,但是,WS-RM對此並沒有嚴格的限製。也就是說,無論所有消息的確認是否成功接收,RM源都可以強行終止對應的序列。此外,除了RM源,RM目的地也可以主動終止當前序列。
為了幫助RM目的地確定它是否成功接收到即將被終止的序列關聯的所有消息,RM源會將自己在在序列上下文中發送的最後一個消息的序號發送給它。所以TerminateSequence元素除了包含封裝有序列標識的<Identifier>子元素之外,還具有必須包含最後消息序號的< LastMsgNumber>元素。下麵是TerminateSequence的結構。
1: <wsrm:TerminateSequence>
2: <wsrm:Identifier> xs:anyURI </wsrm:Identifier>
3: <wsrm:LastMsgNumber> wsrm:MessageNumberType </wsrm:LastMsgNumber> ?
4: ...
5: </wsrm:TerminateSequence>
對於通過<LastMsgNumber>表示的消息序號,還有一點需要特別說明的,那就是該消息序號必須和序列關閉請求消息中CloseSequence元素下的同名元素具有相同的值。下麵的XML片斷展示了一個包含有TerminateSequence元素的SOAP消息。
1: <s:Envelope>
2: <s:Header>
3: <wsa:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/TerminateSequence</wsa:Action>
4: <wsa:MessageID>urn:uuid:3597a398-4f3c-40f4-9335-8f1515572fdf</wsa:MessageID>
5: <wsa:ReplyTo>
6: <wsa:Address>https://www.artech.com/client</wsa:Address>
7: </wsa:ReplyTo>
8: <wsa:To s:mustUnderstand="1">https://www.artech.com/service</wsa:To>
9: </s:Header>
10: <s:Body>
11: <wsrm:TerminateSequence>
12: <wsrm:Identifier>urn:uuid:656652b8-9af2-4e94-9d07-2dc21c05ed27</wsrm:Identifier>
13: <wsrm:LastMsgNumber>30</wsrm:LastMsgNumber>
14: </wsrm:TerminateSequence>
15: </s:Body>
16: </s:Envelope>
當序列終止請求被接收方(RM源或者RM目的地)後,它會回複一個主體部分包含TerminateSequenceReponse元素的消息,TerminateSequenceReponse的結構如下:
1: <wsrm:TerminateSequenceResponse>
2: <wsrm:Identifier> xs:anyURI </wsrm:Identifier>
3: ...
4: </wsrm:TerminateSequenceResponse>
TerminateSequenceReponse元素結構非常簡單,僅僅包含一個表示序列標識的Identifier元素。下麵的XML片斷展示了一個包含有TerminateSequenceReponse元素的SOAP消息。
1: <s:Envelope>
2: <s:Header>
3: <wsrm:SequenceAcknowledgement>
4: <wsrm:Identifier>urn:uuid:656652b8-9af2-4e94-9d07-2dc21c05ed27</wsrm:Identifier>
5: <wsrm:AcknowledgementRange Lower="1" Upper="30"></wsrm:AcknowledgementRange>
6: <wsrm:Final></wsrm:Final>
7: <netrm:BufferRemaining>8</netrm:BufferRemaining>
8: </wsrm:SequenceAcknowledgement>
9: <wsa:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-rx/wsrm/200702/TerminateSequenceResponse</wsa:Action>
10: <wsa:RelatesTo>urn:uuid:3597a398-4f3c-40f4-9335-8f1515572fdf</wsa:RelatesTo>
11: <wsa:To s:mustUnderstand="1">https://www.artech.com/client</wsa:To>
12: </s:Header>
13: <s:Body>
14: <wsrm:TerminateSequenceResponse>
15: <wsrm:Identifier>urn:uuid:656652b8-9af2-4e94-9d07-2dc21c05ed27</wsrm:Identifier>
16: </wsrm:TerminateSequenceResponse>
17: </s:Body>
18: </s:Envelope>
四、Sequence、AckRequested和SequenceAcknowledgement
上麵的內容都在單純地討論RM序列的問題,接下來我們主要討論另外兩個主題:如何將在RM序列上下文中傳輸的消息與序列進行關聯,以及如何實現消息確認。
我們已經知道了,基於WS-RM的消息交換是在實現創建的RM序列上下文中進行的。為了讓RM目的地清楚地知道它所接收的消息具體屬於哪一個RM序列中,需要RM源在發送之前向消息的報頭集合中添加相應的序列關聯信息。此外,與序列關聯信息一並添加的,還有消息的序號。WS-RM將這兩組信息封裝在一個Sequence報頭中,該報頭的XML結構如下。其中Identifier元素的值為RM序列的唯一標識,而MessageNumber即為消息在當前序列上下文中的序號。
1: <wsrm:Sequence>
2: <wsrm:Identifier> xs:anyURI </wsrm:Identifier>
3: <wsrm:MessageNumber> wsrm:MessageNumberType </wsrm:MessageNumber>
4: ...
5: </wsrm:Sequence>
如果RM源希望在RM目的地在接收到某個消息之後對所接收的所有消息進行確認,它會在該消息的報頭集合中添加一個AckRequested報頭。AckRequested的XML結構如下所示,其中僅僅包含一個必須的表示RM序列標識的Identifier元素。
1: <wsrm:AckRequested>
2: <wsrm:Identifier> xs:anyURI </wsrm:Identifier>
3: ...
4: </wsrm:AckRequested>
如果RM目的地接收到一個包含有AckRequested報頭的消息,會對之前成功接收的所有消息進行確認。確認的信息被封裝到一個SequenceAcknowledgement報頭中,該報頭內部包含所有成功接收到的消息的序號。SequenceAcknowledgement報頭具有如下的XML結構。
1: <wsrm:SequenceAcknowledgement ...>
2: <wsrm:Identifier ...> xs:anyURI </wsrm:Identifier>
3: [ [ [ <wsrm:AcknowledgementRange ...
4: Upper="wsrm:MessageNumberType"
5: Lower="wsrm:MessageNumberType"/> +
6: | <wsrm:None/> ]
7: <wsrm:Final/> ? ]
8: | <wsrm:Nack> wsrm:MessageNumberType </wsrm:Nack> + ]
9: ...
10: </wsrm:SequenceAcknowledgement>
上麵的XML片斷可能看起來有點頭暈,為了使讀者能夠很直觀的了解SequenceAcknowledgement報頭的樣式,我們給出幾種典型的實例。下麵的SequenceAcknowledgement意味著對序號1到10的消息的接收確認。
1: <wsrm:SequenceAcknowledgement>
2: <wsrm:Identifier>https://www.artech.com/abc</wsrm:Identifier>
3: <wsrm:AcknowledgementRange Upper="10" Lower="1"/>
4: </wsrm:SequenceAcknowledgement>
下麵的SequenceAcknowledgement則表示對序號從1-2,4-6以及8-10的所有消息的接收確認。
1: <wsrm:SequenceAcknowledgement>
2: <wsrm:Identifier>https://www.artech.com/abc</wsrm:Identifier>
3: <wsrm:AcknowledgementRange Upper="2" Lower="1"/>
4: <wsrm:AcknowledgementRange Upper="6" Lower="4"/>
5: <wsrm:AcknowledgementRange Upper="10" Lower="8"/>
6: </wsrm:SequenceAcknowledgement>
下麵的SequenceAcknowledgement是RM目的地對序號為3的消息的確認,不不過這是一個負麵確認(NACK),意味著序號為3的消息沒有被接收到。
1: <wsrm:SequenceAcknowledgement>
2: <wsrm:Identifier>https://www.artech.com/abc</wsrm:Identifier>
3: <wsrm:Nack>3</wsrm:Nack>
4: </wsrm:SequenceAcknowledgement>
當RM目的地進行消息確認的時候,可以創建一個單獨的空消息,並附加上SequenceAcknowledgement報頭發送給序列確認目標終結點引用(即序列創建請求的CreateSequence元素中AckTo中指定的終結點引用)。如果RM目的地需要向確認目標終結點引用發送消息,它也可以直接將SequenceAcknowledgement報頭附加在該消息之上。這種將為了避免創建新的消息,直接上相應的信息服務加到另一個現有的具有相同目標終結點引用的消息上的方式被稱為“背負(piggy-back)”機製。
到此為止,對WS-RM的介紹就結束了。WS-RM為可靠消息傳輸的實現定義了消息模型,該模型具有很好的可擴展性。不同的WS廠商可以通過自己的方式提供不同的實現方式,但是隻要遵循於WS-RM,就能實現跨廠商和平台的互操作。
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-27 14:35:00