使命必达: 深入剖析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,就能实现跨厂商和平台的互操作。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Script Boy (CN-SIMO)

c++错误代码1

今天敲c++,出现一个错误,这个题目错误代码如下: ? 并没有提示有错误而终止。 运行结果却错了: ? 很明显,错误的地方是变量r没有初始化。 这让我知道了,这...

24200
来自专栏Android 研究

Android系统启动——3init.rc解析

init.rc文件是以“块”(section)为单位服务的,,一个“块”(section)可以包含多行。“块”(section)分成两大类:一类称为"动作(ac...

56120
来自专栏自动化测试实战

RF设置全局变量

53170
来自专栏python学习路

二、路由、模板

一、路由系统 在settings.py文件中通过ROOT_URLCONF指定根级url的配置 urlpatterns是一个url()实例的列表 一个url()对...

35180
来自专栏Pythonista

vim与程序员

所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在。

19020
来自专栏步履前行

深入理解JVM--(1)运行时的数据区域划分-程序计数器

  最近在学习jvm,准备在园子里写个系列笔记,有什么问题大家可以一起探讨。今天学习数据区域划分的第一部分--程序计数器。   JVM在运行时会把管理的内存划...

39060
来自专栏逍遥剑客的游戏开发

Nebula3学习笔记(5): IO系统

19940
来自专栏hbbliyong

C#新功能--命名参数

  命名参数会潜在的改变编写代码的方式.这个新功能能使代码更容易阅读和理解. 例如,看一下System.IO名称空间中的File.Copy()方法,它一般构建为...

28550
来自专栏linux驱动个人学习

Linux进程ID号--Linux进程的管理与调度(三)【转】

Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到进程和程序的所有算法都是围绕该数据结构建立的,...

33010
来自专栏北京马哥教育

经典!Python运维中常用的几十个Python运维脚本

file是一个类,使用file('file_name', 'r+')这种方式打开文件,返回一个file对象,以写模式打开文件不存在则会被创建。但是更推荐使用内置...

46050

扫码关注云+社区

领取腾讯云代金券