使命必达: 深入剖析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">http://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>http://www.artech.com/client</wsa:Address>
7: </wsa:ReplyTo>
8: <wsa:To s:mustUnderstand="1">http://www.artech.com/service</wsa:To>
9: </s:Header>
10: <s:Body>
11: <wsrm:CreateSequence>
12: <wsrm:AcksTo>
13: <wsa:Address>http://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> http://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">http://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">http://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>http://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">http://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>http://www.artech.com/client</wsa:Address>
7: </wsa:ReplyTo>
8: <wsa:To s:mustUnderstand="1">http://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">http://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">http://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">http://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>http://www.artech.com/client</wsa:Address>
7: </wsa:ReplyTo>
8: <wsa:To s:mustUnderstand="1">http://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">http://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">http://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>http://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>http://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>http://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,就能实现跨厂商和平台的互操作。
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。