使命必达: 深入剖析WCF的可靠会话[编程篇](上)
在《实例篇》给出的例子中,我实际上是通过对终结点的绑定进行相应的配置让整个消息的交换过程在一个可靠会话中进行,进而实现可靠消息传输的目的。由于整个可靠会话的机制是完全在信道层实现的,而整个信道层的最终缔造者就是绑定,所以可靠会话编程是围绕着绑定进行的。而从结构组成的角度讲,绑定本质上就是一组绑定元素的有序集合,没有个为了实现各自的目的对传入信道栈的消息进行消息的处理。在这里,实现可靠会话是一个特殊的绑定元素:ReliableSessionBindingElement.
一、从ReliableSessionBindingElement谈起
WCF中整个可靠会话的实现完全是通过ReliableSessionBindingElement这个一个绑定元素创建的信道实现的。具体的实现机制,我们会在《原理篇》进行单独的介绍,在这里我们仅仅讨论可靠会话编程的部分。所以我们先来看看ReliableSessionBindingElement属性定义部分,因为可靠会话实现的行为是受这些属性控制的。
1: public sealed class ReliableSessionBindingElement : BindingElement, IPolicyExportExtension
2: {
3: //其他成员
4: public TimeSpan AcknowledgementInterval { get; set; }
5: public bool FlowControlEnabled { get; set; }
6: public TimeSpan InactivityTimeout { get; set; }
7: public int MaxPendingChannels { get; set; }
8: public int MaxRetryCount { get; set; }
9: public int MaxTransferWindowSize { get; set; }
10: public bool Ordered { get; set; }
11: public ReliableMessagingVersion ReliableMessagingVersion { get; set; }
12: }
虽然我们还没有深入到对可靠会话具体实现机制的介绍,但是通过前面对WS-RM可靠消息传输模型的讨论,相信读者对可靠会话的实现机制会有一个大致的了解。在这里我们可以通过简单地几句话对可靠会话实现机制进行简单的介绍。
WCF的可靠会话是建立在客户端和服务端之间,确保消息可靠传输的上下文,相当于WS-RM中序列的概念。在消息发送端和接收端具有一个消息缓冲区(或者称为消息窗口)对消息进行缓存,前者缓存已经发送但是尚未接收到确认的消息,后者缓存尚未向上交付的消息。消息在发送之前,会被赋予一个特殊的SOAP报头,其中包含表示消息在整个可靠会话生命周期内被发送的序号,并保存到消息缓冲区中。消息被接收端成功接收之后,会向发送端发送一个确认消息表示具有某个序号的消息已经成功接收。如果需要保障有序交付,接收端在交付之前需要确定先于该消息发送的所有消息是否成功交付。如果是则实施交付,否则将其放入消息缓冲区,等待之前消息的抵达。当之前所有消息被成功接收之后,接收端按照消息序号从小到大的顺序对消息实施交付。缓存的消息被成功交付后,会从缓冲区移除。 而消息发送端在接收到消息确认之后,会根据消息序号将对应的消息从缓冲区冲移出。如果在限定的超时时限内没有接收到以发消息的确认,会认为该消息发送失败,该消息会从缓冲区中提取出来并重新发送。
那么,现在我们了解了WCF可靠会话大体机制的情况下来了解一下ReliableSessionBindingElement各个属性的含义。
- AcknowledgementInterval:如果消息的发送方和接收方通过双工通道连接,接收方能够随时向接收方发送确认。在这种情况下,为了降低网络流量,WCF采用批量确认的机制。也就是说,当接收端成功接收到某个消息的时候,并不会立即针对该消息发送确认,而是等待一定时间后,对所有接收到的消息进行批量确认。AcknowledgementInterval表示消息接收端发送确认之前等待的时间间隔,默认为0.2秒(200毫秒)。该设置仅仅针对于NetTcpBingding和WSDualHttpBiding等支持双工通信的绑定有效,而像BasicHttpBinding和WSHttpBiding这样的绑定无此设置。
注:在MSDN对该属性的介绍中,将AcknowledgementInterval得默认值说成是2秒,这是不对的。
- FlowControlEnabled:该属性指示可靠会话是否已启用流控制(Flow Control)。流控制是可靠会话实现的一种机制,目的是确保发送方所发送的消息数不超过接收方可处理的消息数。接收方拥有消息缓冲区,用于容纳突增的消息和无序的消息。接收方在每次确认时都会告知发送方此缓冲区中的剩余空间量。利用此信息,发送方就可以在接收方缓冲区中没有剩余空间时,停止发送新的消息。FlowControlEnabled默认值为True。
- InactivityTimeout:在WS-RM中,被创建的RM序列具有一个Expires属性表示序列的生命周期。但是,我们不能通过可靠会话的整个生命周期的时限,也就是,整个可靠会话并没有具体时间的限制。但是,WCF并不能依赖于可靠会话被显式地被终止,而设定一个超时时限,在该时间范围内如何没有活动的消息交换,WCF会将可靠会话关闭。InactivityTimeout就是这么一个表示可靠会话在关闭之前保持非活动状态的时间间隔,默认值为10分钟。
- MaxPendingChannels:通道在等待被接受时处于挂起状态。一旦达到该限制,就不会创建任何通 道并将其置于挂起模式,直到此数值降低(通过接受挂起的通道)。这是对每个侦听器的限制。当达到此阈值时如果远程应用程序尝试建立新的可靠会话,则会拒绝请求且打开操作将提示此错误。这么一个可靠会话期间可为挂起状态的最大通道数通过MaxPendingChannels表示,默认值为4;
- MaxRetryCount:如果在某个可接受时间范围内,消息的发送端没有接收到某个以发消息的确认,会对该消息进行重传。MaxRetryCount表示重传的次数,最小值为1,最大值为0x7FFFFFFF(Int32.MaxValue)的默认值为8。WCF使用指数补偿算法根据计算的平均往返时间来确定何时重新传输。初始重试时间是根据测量的创建会话往返时间来计算的。重新传输算法是每进行一次尝试,延迟时间就会增加一倍。如此一来,从第一次传输尝试到最后一次传输尝试大约会历时 8.5 分钟。可以根据计算的往返时间来调整第一次尝试重新传输的时间,因此这些尝试所经历的时间将会相应地发生变化。这样,可以使重新传输时间动态地适应不断变化的网络条件。MaxRetryCount表示进行重传的次数,在达到此限制之前未接收到确认被视为严重的通信故障,这种情况将引发通道出错的事件。
- MaxTransferWindowSize:该属性表示消息缓冲区的大小,或者说是缓冲区能够容纳消息的数量。MaxTransferWindowSize最小值为1,最大值为4096,默认值为32。MaxTransferWindowSize属性的值可在发送方和接收方进行设置。如果达到发送方的这个限制,则会阻止其他发送调用。如果达到接收方的这个限制,则不会接受到达基础通道的新消息。可靠会话会使用窗口 协议来帮助提高网络利用率。所需的缓冲量直接从传输窗口的大小派生而来。调整此值时请注意,最佳传输窗口直接与带宽和延迟相关。理想的最大窗口大小是带宽乘以延迟。如果比此值小,网络利用率就会低于 100%,如果比此值大,就会浪费空间。
- Ordered:该属性表示是否启用“有序传输”机制确保消息的接收端完全按照消息被发送的顺序进行交付;默认值为False;
- ReliableMessagingVersion:该属性表示可靠会话支持的WS-RM的版本。在WCF中,该WS-RM通过ReliableMessagingVersion类型表示(不是枚举)。ReliableMessagingVersion定义如下,我们可以通过两个静态只读属性WSReliableMessagingFebruary2005和WSReliableMessaging11得到两个表示WS-RM 1.0和WS-RM 1.1的ReliableMessagingVersion对象。静态只读属性Default表示默认的WS-RM版本,其值目前和WSReliableMessagingFebruary2005属性一致。
1: public abstract class ReliableMessagingVersion
2: {
3: public static ReliableMessagingVersion Default { get; }
4: public static ReliableMessagingVersion WSReliableMessagingFebruary2005 { get; }
5: public static ReliableMessagingVersion WSReliableMessaging11 { get; }
6: }
对ReliableSessionBindingElement绑定元素有了一定的了解之后,我们来谈谈包含该绑定元素的绑定。先来了解一下系统绑定。
二、为系统绑定的可靠会话进行设置
在众多系统绑定中,有很大一部分都为可靠会话提供支持,比如WSHttpBinding、WS2007HttpBinding、NetTcpBinding、WSFederationHttpBinding、WS2007FederationHttpBinding和WSDualHttpBinding。这些绑定类型中均有一个的名称为ReliableSession的属性,属性类型为ReliableSession或者其子类OptionalReliableSession。下面的代码演示了WSHttpBindingBase(WSHttpBinding和WS2007HttpBinding的基类)和WSDualHttpBinding的ReliableSession属性的定义。
1: public abstract class WSHttpBindingBase : Binding, IBindingRuntimePreferences
2: {
3: //其它成员
4: public OptionalReliableSession ReliableSession { get; }
5: }
6:
7: public class WSDualHttpBinding : Binding, IBindingRuntimePreferences
8: {
9: //其它成员
10: public ReliableSession ReliableSession { get; }
11: }
在讨论不同类型的系统绑定对可靠会话的支持之前,我们不妨先来看看ReliableSession和OptionalReliableSession的定义。从下面的定义,我们可以看出ReliableSession定义非常简单。仅仅具有两个可读写的属性InactivityTimeout和Ordered,不同多说读者也知道这和前面介绍的ReliableSessionBindingElement的同名属性相匹配,而整个ReliableSession对象是根据ReliableSessionBindingElement对象创建的。ReliableSession的子类OptionalReliableSession多了一个额外可读写的属性Enabled,这是一个让绑定启用可靠会话的开关。
1: public class ReliableSession
2: {
3: public ReliableSession(ReliableSessionBindingElement reliableSessionBindingElement);
4: public TimeSpan InactivityTimeout { get; set; }
5: public bool Ordered { get; set; }
6: }
7:
8: public class OptionalReliableSession : ReliableSession
9: {
10: public OptionalReliableSession(ReliableSessionBindingElement reliableSessionBindingElement);
11: public bool Enabled { get; set; }
12: }
虽然在ReliableSessionBindingElement绑定元素中为我们定义了众多控制可靠会话行为的属性,但是ReliableSession中仅仅为我们公布了其中两个:InactivityTimeout和Ordered。潜在的信心告诉我们,对于这些支持可靠会话的系统绑定来说,我们只能设置可靠会话在关闭之前保持非活动状态的时间间隔和开启或者关闭有序交付特性。其他选项,比如支持的WS-RM版本,以及消息缓冲区大小,都是系统为我们定制的,不能修改。
对于前面提到的若干支持可靠会话的系统绑定,除了WSDualHttpBinding的ReliableSession属性类型为ReliableSession外,其余的均为OptionalReliableSession。也就是我们不能关闭WSDualHttpBinding的可靠会话特性,它总是按照可靠会话的机制进行消息的交换。WCF之所以如此涉及,是源于WSDualHttpBinding支持双工通信的特殊机制决定的。由于基于请求/回复模式的HTTP传输不能够独立提供对双工通信的支持,WCF采用的是双通道的方式。也就是说,对于通过WSDualHttpBinding创建的所谓的双工通道是由两个方向相反的HTTP连接组成的,WCF采用可靠会话机制提供对这两个连接的匹配。
除了InactivityTimeout和Ordered两个属性可以进行设置之外,定义在ReliableSessionBindingElement绑定元素中的各个属性大多采用默认值。但是,有一个例外,即表示支持WS-RM版本的ReliableMessagingVersion属性。对于WSHttpBinding、WSDualHttpBinding和WSFederationHttpBinding支持的版本是WS-RM 1.0,而WS2007HttpBinding和WS2007FederationHttpBinding则支持的是WS-RM 1.1。
我们可以通过编程的方式开启或者关闭终结点使用的除WSDualHttpBinding之外的其他系统绑定(限于支持可靠会话系统绑定)的可靠会话开关,以及设置InactivityTimeout和Ordered属性。不过,我们最好还是采用配置的方式对可靠会话进行设置。可靠会话相关配置定义在具体绑定配置中的reliableSession结点中。下面的配置中,我们在客户端对终结点使用的WS2007HttpBinding的可靠会话进行了设置。
1: <system.serviceModel>
2: <bindings>
3: <ws2007HttpBinding>
4: <binding name="reliableSession2007Binding">
5: <reliableSession enabled="True" inactivityTimeout="00:20:00" ordered="True"/>
6: <security mode="None"/>
7: </binding>
8: </ws2007HttpBinding>
9: </bindings>
10: <client >
11: <endpoint name="calculatorservice" address="http://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" bindingConfiguration="reliableSession2007Binding" contract="Artech.MessageInspection.Sender.ICalculator" />
12: </client>
13: </system.serviceModel>
绑定是一系列绑定元素的有序组合,但是系统绑定为我们提供适应了某种典型通信环境的绑定元素组合方式,可以看成是“套餐”。但是,如果套餐不符合您的胃口,你应该查看菜单点你喜欢的菜肴。自定义绑定给了你最大的自由度,是能能够根据具体的通信环境自由组合需要的绑定元素。关于如何为自定义绑定的可靠会话进行设置,敬请关注《下篇》。
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。