可靠性
WCF与其他面向服务技术之间最大的区别在于传输可靠性(Transport Reliability)与消息可靠性(Message Reliability)。传输可靠性(例如通过TCP传输)在网络数据包层提供了点对点保证传递(Point-to-Point Guaranteed Delivery),以确保数据包的顺序无误。传输可靠性不会受到网络连接的中断或其他通信问题的影响。
顾名思义,消息可靠性负责处理消息层的可靠性,它与传递消息的数据包数量无关。消息可靠性提供了端对端保证传递(End-to-End Guaranteed Delivery),确保消息的顺序无误。消息可靠性与引入的中间方的数量无关,与网络跳数(Network Hops)的数量也没有关联。消息可靠性基于一个行业标准。该行业标准为可靠的基于消息的通信维持了一个在传输层的会话。如果传输失败,例如无线连接中断,消息可靠性就会提供重试(Retries)功能。它还能够自动处理网络阻塞(Congestion)、消息缓存(Message Buffering)以及流控制(Flow Control),根据具体情况适时调整发送的消息数。消息可靠性还能够通过对连接的验证管理连接自身,并在不需要连接时清除它们。
绑定与可靠性
WCF的可靠性是在绑定中控制与配置的。一个特定的绑定可以支持可靠消息传输(Reliable Messaging),也可以不支持它。如果支持,也可以通过设置为启用或禁用。何种绑定支持何种可靠性值,要根据绑定的目标场景而定。表1-2总结了绑定、可靠性、有序传递(Ordered Delivery)以及它们各自的默认值之间的关系。
表1-2:可靠性与绑定
名称 支持可靠性 默认可靠性 支持有序传递 默认有序传递
BasicHttpBinding No N/A No N/A
NetTcpBinding Yes Off Yes On
NetPeerTcpBinding No N/A No N/A
NetNamedPipeBinding No N/A (On) Yes N/A (On)
WSHttpBinding Yes Off Yes On
WSFederationHttpBinding Yes Off Yes On
WSDualHttpBinding Yes On Yes On
NetMsmqBinding No N/A No N/A
MsmqIntegrationBinding No N/A No N/A
BasicHttpBinding、NetPeerTcpBinding以及两种MSMQ绑定(NetMsmqBinding和MsmqIntegrationBinding)不支持可靠性。因为BasicHttpBinding面向旧的ASMX Web服务,是不具有可靠性的。NetPeerTcpBinding则为广播场景设计。MSMQ绑定针对断开调用,在任何情况下都不存在传输会话。
WSDualHttpBinding总是支持可靠性的,它能够保持回调通道,确保基于HTTP协议的客户端存在。
NetTcpBinding绑定以及各种WS绑定,默认情况下并不支持可靠性,但是允许启用对它的支持。由于NetNamedPipeBinding绑定总是拥有一个确定的从客户端到服务的跳数,因而它的可靠性是绑定固有的。
有序消息
消息可靠性确保了消息的有序传递,允许消息按照发送顺序而非接收顺序执行。此外,它保证了消息只会被传递一次。
WCF允许开发者只启用可靠性,而不启用有序传递。此时,消息按照接收它们的顺序进行传递。如果同时启用了可靠性与有序传递,则所有绑定的默认值均支持可靠性。
配置可靠性
通过编程方式或管理方式都可以配置可靠性(以及有序传递)。如果我们启用了可靠性,则客户端与服务宿主端必须保持一致,否则客户端无法与服务通信。我们可以只对支持它的绑定配置可靠性。例1-23所示的服务端配置文件,使用了绑定配置节,启用了TCP绑定的可靠性。
例1-23:启用TCP绑定的可靠性
<system.serviceModel>
<services>
<service name = "MyService">
<endpoint
address = "net.tcp://localhost:8000/MyService"
binding = "netTcpBinding"
bindingConfiguration = "ReliableTCP"
contract = "IMyContract"
/>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name = "ReliableTCP">
<reliableSession enabled = "true"/>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
至于编程配置方式,TCP绑定和WS绑定提供了略微不同的属性来配置可靠性。例如,NetTcpBinding绑定接受一个Boolean型的构造函数参数,用来启动可靠性:
public class NetTcpBinding : Binding,...
{
public NetTcpBinding(...,bool reliableSessionEnabled);
//更多成员
}
我们只能在对象的构造期间启用可靠性。如果通过编程方式设置可靠性,需要创建支持可靠性的绑定对象:
Binding reliableTcpBinding = new NetTcpBinding(...,true);
NetTcpBinding定义了只读的ReliableSession类,通过它获取可靠性的状态:
public class ReliableSession
{
public TimeSpan InactivityTimeout
{get;set;}
public bool Ordered
{get;set;}
//更多成员
}
public class OptionalReliableSession : ReliableSession
{
public bool Enabled
{get;set;}
//更多成员
}
public class NetTcpBinding : Binding,...
{
public OptionalReliableSession ReliableSession
{get;}
//更多成员
}
必备有序传递
理论上,服务代码和契约定义应该与它使用的绑定及属性无关。服务不应该考虑绑定,在服务代码中也不应该包含它所使用的绑定。不管配置的绑定是哪一种,服务都应该能够正常工作。然而实际上,服务的实现或者契约本身都会依赖于消息的有序传递(Ordered Delivery)。为了帮助契约或服务的开发者能够约束支持的绑定,WCF定义了DeliveryRequirementsAttribute特性:
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface
AllowMultiple = true)]
public sealed class DeliveryRequirementsAttribute : Attribute,...
{
public Type TargetContract
{get;set;}
public bool RequireOrderedDelivery
{get;set;}
//更多成员
}
DeliveryRequirements特性可以应用到服务一级,对服务的所有终结点施加影响,或者只对公开了特定契约的终结点施加影响;如果应用到服务一级,则意味着选用有序传递是根据具体实现作出的决策。DeliveryRequirements特性也可以应用到契约一级,它会对所有支持该契约的服务施加影响。这样一种在契约一级的应用,体现了对有序传递的要求是根据设计作出的决策。这一约束会在装载服务时得到执行与验证。如果一个终结点包含的绑定并不支持可靠性;或者支持可靠性,却被禁用了;或者虽然启用了可靠性,但却禁用了有序传递,那么装载服务就会失败,抛出InvalidOperationException异常。
注意:命名管道绑定符合有序传递的约束。
举例来说,如果不考虑契约,要求服务的所有终结点都启用有序传递,则可以将DeliveryRequirements特性直接应用到服务类上:
[DeliveryRequirements(RequireOrderedDelivery = true)]
class MyService : IMyContract,IMyOtherContract
{...}
通过设置TargetContract属性,只有支持目标契约的服务终结点才需要遵循可靠的有序传递的约束:
[DeliveryRequirements(TargetContract = typeof(IMyContract),
RequireOrderedDelivery = true)]
class MyService : IMyContract,IMyOtherContract
{...}
如果将DeliveryRequirements特性应用到契约接口上,则支持该契约的所有服务都必须遵循这一约束:
[DeliveryRequirements(RequireOrderedDelivery = true)]
[ServiceContract]
interface IMyContract
{...}
class MyService : IMyContract
{...}
class MyOtherService : IMyContract
{...}
RequireOrderedDelivery的默认值为false,如果只应用了DeliveryRequirements特性,没有设置RequireOrderedDelivery的值,则是无效的。例如,如下语句是等效的:
[ServiceContract]
interface IMyContract
{...}
[DeliveryRequirements]
[ServiceContract]
interface IMyContract
{...}
[DeliveryRequirements(RequireOrderedDelivery = false)]
[ServiceContract]
interface IMyContract
{...}





