本系列先后通过《实例篇》、《概念篇》、《协议篇》和《编程篇》对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信道之间。这其中不仅仅包括村存在于客户端和服务端之间的传输网络,也包括存在可靠会话信道之下的所有信道。相信大家还记得《实例篇》中的那个实例演示,我们用于模拟不稳定网络环境的信道对应的绑定元素最终配置在可靠会话绑定元素之后。如果将其移到可靠会话绑定元素之前,由该自定义信道导致的消息丢失的问题将不能通过可靠会话解决。
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="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <a:Action s:mustUnderstand="1">http://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">http://www.artech.com/calculatorservice</a:To>
6: </s:Header>
7: <s:Body>
8: <CreateSequence xmlns="http://docs.oasis-open.org/ws-rx/wsrm/200702">
9: <AcksTo>
10: <a:Address>http://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>http://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="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
2: <s:Header>
3: <a:Action s:mustUnderstand="1">http://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="http://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>http://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="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="http://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">http://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>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
11: </a:ReplyTo>
12: <a:To s:mustUnderstand="1">http://www.artech.com/calculatorservice</a:To>
13: </s:Header>
14: <s:Body>
15: <Add xmlns="http://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="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="http://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="http://schemas.microsoft.com/ws/2006/05/rm">8</netrm:BufferRemaining>
11: </r:SequenceAcknowledgement>
12: <a:Action s:mustUnderstand="1">http://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="http://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="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="http://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">http://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>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
15: </a:ReplyTo>
16: <a:To s:mustUnderstand="1">http://www.artech.com/calculatorservice</a:To>
17: </s:Header>
18: <s:Body>
19: <Add xmlns="http://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所示。
四、 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="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="http://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">http://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">http://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="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="http://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="http://schemas.microsoft.com/ws/2006/05/rm">8</netrm:BufferRemaining>
8: </r:SequenceAcknowledgement>
9: <a:Action s:mustUnderstand="1">http://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="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="http://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">http://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">http://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="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://docs.oasis-open.org/ws-rx/wsrm/200702" xmlns:a="http://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="http://schemas.microsoft.com/ws/2006/05/rm">8</netrm:BufferRemaining>
8: </r:SequenceAcknowledgement>
9: <a:Action s:mustUnderstand="1">http://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="http://schemas.microsoft.com/ws/2006/05/rm">8</netrm:BufferRemaining>
6: </r:SequenceAcknowledgement>
由于WCF的可靠会话完全是在信道层实现的,而信道层就是由一系列用于处理消息的信道组成,所有从消息在信道层的交换可以帮助我们很容易地从本质上把握可靠会话的实现。在目前所有关于WCF的著作中,没有一本能够站在如此低层次地对可靠消息的实现进行剖析。作为一本深入剖析WCF实现机制的文章,我们还此基础上对其进行进一步的挖掘。在《下篇》中,我们从传输协议的角度对可靠会话的实现进行进一步的剖析,敬请期待。
共同学习,写下你的评论
评论加载中...
作者其他优质文章