在这篇文章中,我们对信道分发器本身作一个深入的了解,首先来看看它具有哪些可供扩展的组件,以及我们可以针对信道分发器对WCF实现哪些可能的扩展。
目录:
ErrorHandler & ServiceThrottle
ChannelInitializer
IncludeExceptionDetailInFaults
ManualAddressing
MaxPendingReceives
ReceiveSynchronously
IsTransactedReceive & MaxTransactedBatchSize
TransactionIsolationLevel & TransactionTimeout
信道分发器对应的类型为ChannelDispatcher,下面的代码片断给出了ChannelDispatcher部分属性成员的定义。而这些属性代表了包含在信道分发器中那些可供扩展的组件。信道分发器是基于信道监听器创建的,后者用于请求消息的监听和消息接收信道栈的创建。信道监听器对应于只读属性Listener。
1: public class ChannelDispatcher : ChannelDispatcherBase
2: {
3: //其他成员
4: public SynchronizedCollection<IChannelInitializer> ChannelInitializers { get; }
5: public Collection<IErrorHandler> ErrorHandlers { get; }
6: public ServiceThrottle ServiceThrottle { get; set; }
7:
8: public override IChannelListener Listener { get; }
9: }
ErrorHandler & ServiceThrottle
而属性ErrorHandlers代表的是一组ErrorHandler对象的集合。而ErrorHandler用于异常的处理的错误消息的提供。而类型为ServiceThrottle的同名属性用于进行流量控制,相关的内容你也可以参考《WCF中并发(Concurrency)与限流(Throttling)》。
ChannelInitializer
至于属性ChannelInitializers,则代表的是一组实现了接口System.ServiceModel.Dispatcher.IChannelInitializer的被称为信道初始化器的对象。顾名思义,所谓信道初始化器,就是当服务信道被创建之后用于对其进行初始化操作。接口IChannelInitializer的定义如下,它只具有一个唯一的Initialize方法。
1: public interface IChannelInitializer
2: {
3: void Initialize(IClientChannel channel);
4: }
从扩展性角度来讲,你可以将自定义的ErrorHandler和ServiceThrottle应用到信道分发器中分别实现对异常的处理和流量的控制。你也可以自定义信道初始化器改变创建的信道状态。上述的关于信道分发器的结构可以简单地通过下图表示。
为了实现自定义的异常处理和流量扩展等功能,你可以将自定义的相关组件应用到信道分发器中。另一方面,信道分发器本身具有一些用于控制器运行行为的属性。你也可以根据需要改变这些属性是信道分发器按照你希望的行为进行运作。下面的代码片断列出了信道分发器主要的可供修改的属性。其中通过属性MessageVersion表示的消息的版本(SOAP版本和WS-Addressing版本)决定于绑定的同名属性。
1: public class ChannelDispatcher : ChannelDispatcherBase
2: {
3: //其他成员
4: public bool IncludeExceptionDetailInFaults { get; set; }
5: public bool ManualAddressing { get; set; }
6: public MessageVersion MessageVersion { get; set; }
7: public int MaxPendingReceives { get; set; }
8: public bool ReceiveSynchronously { get; set; }
9: public bool IsTransactedReceive { get; set; }
10: public int MaxTransactedBatchSize { get; set; }
11: public IsolationLevel TransactionIsolationLevel { get; set; }
12: public TimeSpan TransactionTimeout { get; set; }
13: }
IncludeExceptionDetailInFaults
IncludeExceptionDetailInFaults:表示服务端抛出的异常的详细信息是否需要通过错误消息回传给客户端。基于安全的需要,该属性的默认值为False。通常只有在调试的时候我们才需要让客户端得到服务端原始的错误信息,所以这个开关由服务行为ServiceDebugBehavior来控制。如下面的代码所示,ServiceDebugBehavior具有一个同名的属性。你也可以直接通过在服务类型上应用ServiceBehaviorAttribute特性通过命名属性控制这个开关。关于该属性背后的原理,你可以参考我的文章《ServiceDebugBehavior服务行为是如何实现异常的传播的?》
1: public class ServiceDebugBehavior : IServiceBehavior
2: {
3: //其他成员
4: public bool IncludeExceptionDetailInFaults { get; set; }
5: }
6: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
7: {
8: //其他成员
9: public bool IncludeExceptionDetailInFaults { get; set; }
10: }
ManualAddressing
而属性ManualAddressing则涉及到寻址(Addressing)的概念。对于一个支持WS-Addressing的SOAP消息来说,在其报头列表中包括一系列WS-Addressing报头(比如To、ReplyTo、RelatesTo等)以提供消息路由需要的寻址信息。在默认的情况下,这些寻址报头最终是通过位于信道栈最底层的传输信道(Transport
Channel)来添加的。但是在某些情况下,我们希望手工地位消息添加相应的寻址报头,并希望该消息按照这些手工添加的寻址信息进行路由。我们将这种机制成为手工寻址(Manual
Addressing)。
如果启用手工寻址,当消息最终通过传输信道向传输层发送的时候,传输信道会认为相应的寻址报头已经被成功添加,所以不会进行寻址报头的重复添加。ChannelDispatcher的ManualAddressing属性表示是否启用了手工寻址,其默认值决定于绑定的传输绑定元素的同名属性。按照寻址的需要,你可以在运行时动态变该属性值强制启用或者禁用手工寻址。
1: public abstract class TransportBindingElement : BindingElement
2: {
3: //其他成员
4: public bool ManualAddressing { get; set; }
5: }
MaxPendingReceives
MaxPendingReceives表示允许的最大挂起(未处理)的消息数,默认值为1。该值可以通过终结点行为DispatcherSynchronizationBehavior来修改。如下所示,DispatcherSynchronizationBehavior具有一个同名的属性。
1: public class DispatcherSynchronizationBehavior : IEndpointBehavior
2: {
3: //其他成员
4: public int MaxPendingReceives { get; set; }
5: }
ReceiveSynchronously
对于服务端信道层对请求消息的接收,到底采用同步还是异步的方式更加有效往往取决于具体采用的通信方式。在默认的情况下,对于同步/异步消息接收方式的选择取决于终结点的绑定。对于所有的系统预定义绑定类型,它们都实现了一个特殊的接口IBindingRuntimePreferences。如下面的代码片断所示,IBindingRuntimePreferences接口具有一个唯一的只读属性:ReceiveSynchronously。该属性就表示具体的绑定是否应该采用同步的消息接收方式。而在默认的情况下,绑定的ReceiveSynchronously属性值被作为对应的信道分发器的同名属性值。
1: public interface IBindingRuntimePreferences
2: {
3: bool ReceiveSynchronously { get; }
4: }
对于几个我们常用的系统预定义绑定(BasicHttpBinding、WSHttpBinding、WSHttp2007Binding、WSDualHttpBinding、NetTcpBinding、NetNamedPipeBinding和NetMsmqBinding),除了NetMsmqBinding的ReceiveSynchronously属性可以是True外,其他绑定的该属性总是返回False。也就是说,除了NetMsmqBinding,其他的绑定总是以异步的方式进行消息的接收,这样可以及时地处理同时抵达的消息请求,并极大的改善服务的吞吐量。而对于NetMsmqBinding来说,它的ReceiveSynchronously属性和ExactlyOnce具有相同的值。
虽然在默认的情况下,绑定的ReceiveSynchronously属性决定了信道分发器的同名属性。但是你也可以通过扩展来改变该属性值。实际上,WCF为我们定义了一个类型为System.ServiceModel.Description.SynchronousReceiveBehavior的终结点行为来对信道分发器的ReceiveSynchronously属性值进行设定。当终结点应用了该行为之后,对应的信道分发器被自动设置为True,意味着采用同步的方式接收请求消息。
IsTransactedReceive & MaxTransactedBatchSize
接下来,我们来关于信道分发器与事务相关的几个属性。首先是基于事务的消息接收。为了判断某个绑定是否支持事务性消息接收,WCF定义了名称为System.ServiceModel.Channels.ITransactedBindingElement的接口。从下面给出的定义可以看出,ITransactedBindingElement具有唯一的只读属性TransactedReceiveEnabled,表明是否需要将消息的接收工作纳入到事务中进行。
1: public interface ITransactedBindingElement
2: {
3: bool TransactedReceiveEnabled { get; }
4: }
从接口的名称我们就可以看出来,ITransactedBindingElement是为绑定元素定义的接口。对于一个具体的绑定来说,只要它的绑定元素列表中具有任何一个绑定元素实现了ITransactedBindingElement接口,并且TransactedReceiveEnabled属性返回True,就意味着这是一个基于事务性消息接收的绑定。而绑定的是否支持事务性消息接收在默认的情况下反应在信道分发器的IsTransactedReceive属性上,而另一个属性MaxTransactedBatchSize则表示允许纳入同一个事务进行的最大消息接收操作数。对于WCF预定义的所有绑定元素,只有基于MSMQ的两个绑定元素MsmqTransportBindingElement和MsmqIntegrationBindingElement实现了ITransactedBindingElement接口。
对于最初决定于绑定的这两个基于事务性消息接收的属性,我们也可以通过扩展对其进行动态修改以强制或者避免进行事务性消息接收。而对于MaxTransactedBatchSize属性的设定,WCF同样为我们定义了相应的终结点属性:System.ServiceModel.Description.TransactedBatchingBehavior。信道分发器的MaxTransactedBatchSize对应于TransactedBatchingBehavior的MaxBatchSize属性。
1: public class TransactedBatchingBehavior : IEndpointBehavior
2: {
3: //其他成员
4: public int MaxBatchSize { get; set; }
5: }
TransactionIsolationLevel & TransactionTimeout
如果你阅读看了我的文章《WCF事务编程([上篇]、[中篇]、[下篇])》你应该对信道分发器的另外两个基于事务的属性TransactionIsolationLevel和TransactionTimeout不会感到陌生。它们代表在事务的隔离级别和超时时限。这两个属性对应于我们熟悉的ServiceBehaviorAttribute特性的同名属性。
1: [AttributeUsage(AttributeTargets.Class)]
2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
3: {
4: //其他成员
5: public IsolationLevel TransactionIsolationLevel { get; set; }
6: public string TransactionTimeout { get; set; }
7: }
作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。