WCF服务端运行时架构体系详解[中篇]

在这篇文章中,我们对信道分发器本身作一个深入的了解,首先来看看它具有哪些可供扩展的组件,以及我们可以针对信道分发器对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的两个绑定元素MsmqTransportBindingElementMsmqIntegrationBindingElement实现了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: }

WCF服务端运行时架构体系详解[上篇]

WCF服务端运行时架构体系详解[中篇]

WCF服务端运行时架构体系详解[下篇]

WCF服务端运行时架构体系详解[续篇]

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-11-05 05:04:14

WCF服务端运行时架构体系详解[中篇]的相关文章

WCF服务端运行时架构体系详解[下篇]

作为WCF中一个核心概念,终结点在不同的语境中实际上指代不同的对象.站在服务描述的角度,我们所说的终结点实际上是指ServiceEndpoint对象.如果站在WCF服务端运行时框架来说,终结点实际上指代的是终结点分发器(EndpointDispatcher).而ServiceEndpoint与EndpointDispatcher是一一匹配的,并且前者是创建后者的基础.而终结点分发器具有自己的运行,即分发运行时(DispatchRuntime). 目录 一.终结点分发器(EndpointDispa

WCF服务端运行时架构体系详解[上篇]

WCF的服务端架构体系又可以成为服务寄宿端架构体系.我们知道,对于一个基于某种类型的服务进行寄宿只需要使用到一个唯一的对象,那就是ServiceHost.甚至在某种语境下,我们所说的服务实际上就是指的对应的ServiceHost对象.整个服务寄宿过程包括两个阶段,即服务描述的创建和服务端运行框架的建立.而第一个阶段创建的服务描述是为了第二个阶段对服务端运行时框架建立服务的,所以我们有必要在对服务描述进行简单的介绍. 目录: 一.从服务描述(Service Description)谈起 二.服务端

WCF服务端运行时架构体系详解[续篇]

终结点分发器在自己的运行时中对请求消息的处理最终肯定体现在相应操作的执行.如果从服务描述的角度来看,操作是一个OperationDescription对象.而服务端分发运行时中的操作则代表的是一个DispatchOperation对象.作为服务描述的一部分,服务所有终结点的所有操作描述(OperationDescription)在ServiceHost创建过程中被创建.而当ServiceHost被正常开始时,这些操作描述最终转换成分发操作(DispatchOperation).而Dispatch

WCF客户端运行时架构体系详解[上篇]

客户端调用WCF服务的方式不外乎有两种:其一.通过代码生成工具(比如SvcUtil.exe)导入服务的元数据生成服务代理相关的类型:其二.通过ChannelFactory<TChannel>创建服务代理对象.对于前者,生成的服务代理是一个继承自ClientBase<TChannel>的类型.对于这样一个服务代理对象,其内部本质上还是借助于ChannelFactory<TChannel>创建真正用于进行服务调用的代理对象.对于WCF客户端应用编程接口来说,ChannelF

WCF客户端运行时架构体系详解[下篇]

当基于某个终结点创建的ChannelFactory<TChannel>被开启的之后,位于服务模型层的客户端运行时框架被成功构建.站在编程的角度看ChannelFactory<TChannel>,它就是一个创建用于服务调用的服务代理对象的工厂.由于服务调用需要借助于服务代理来完成,我们很有必要从整个客户端运行架构层面来了解服务代理和基于服务代理的服务调用是如何实现的. 目录 一.服务代理是一个透明代理 二.服务调用的流程       操作选择       输入参数检验       序

ASP.NET HTTP运行时组成的详解

asp.net|详解 简介 不管使用哪种底层平台,可靠性和性能都是对所有 Web 应用程序的主要要求,尽管从某种意义上讲,这两个要求是相互矛盾的.例如,要构建更可靠.更健壮的应用程序,可能需要将 Web 服务器与具体的应用程序分离,使应用程序在进程外工作.但是,如果在不同于 Web 服务器进程的内存环境中工作,应用程序将变慢.因此,需要采取合理的措施,以确保进程外代码尽可能快地运行. 在构建 Microsoft? ASP.NET 运行时环境时,依据的设计原则即:充分考虑可靠性和性能.得到的 AS

C++:RTTI(RunTime Type Information)运行时类型信息 详解

RTTI, RunTime Type Information, 运行时类型信息, 是多态的主要组成部分, 通过运行时(runtime)确定使用的类型, 执行不同的函数,复用(reuse)接口. dynamic_cast<>可以 使基类指针转换为派生类的指针, 通过判断指针的类型, 可以决定使用的函数. typeid(), 可以判断类型信息, 判断指针指向位置, 在多态中, 可以判断基类还是派生类. 代码: /* * test.cpp * * Created on: 2014.04.22 * A

android与asp.net服务端共享session的方法详解

前言 最近因为工作的需要,要实现一个功能,就是需要通过发送短信进行注册,现在想把短信验证码放到服务器的session值中,当客户端收到短信并提交短信码时由asp.net服务端进行判断,那么如何共享这个session那么需要在android客户端中添加几行代码. 实现方法 如下操作.第一次数据请求时就获取这个cookie的名称并且得到这个cookie的值,这个即是sessionid的值并保存在一个静态变量中,然后在第二次请求数据的时候要将这个sessionid一并放在Cookie中发给服务器,服务

java程序运行时内存分配详解_java

一. 基本概念    每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一对应一个堆,每一个线程有一个自己私有的栈.进程所创建的所有类的实例(也就是对象)或数组(指的是数组的本身,不是引用)都放在堆中,并由该进程所有的线程共享.Java中分配堆内存是自动初始化的,即为一个对象分配内存的时候,会初始化这个对象中变量.虽然Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在栈中分配,也