原文地址 http://www.cnblogs.com/idior/articles/971252.html

介绍

WCF具有非常易用的编程模型,服务开发者在掌握ABC的概念后可以很容易的使用WCF去实现他们的服务。同时也具有极高的扩展性,比如说,如果你想给你的服务添加一些安全相关的特性,只需要给你的服务或者是操作加上一些相应的attribute即可。

但是,你有没有想过,当你在给一个方法头上加了OperationContract特性,或者你给ServiceContract特性加了一些参数后,WCF服务运行时究竟会做什么。WCF将一切变得简单,但是这往往让我们抓狂,我们很想知道WCF内部的复杂的实现究竟是怎么搞的,WCF服务究竟是怎么运作的。这篇文章就是为了阐述这些的。

你知道一个服务在开启的时候会做什么吗?怎样从传输层去获取消息并将其分发到相应的服务实例?这些对于服务开发者来说都是透明的我们只需要做的就是实现服务契约,打开ServiceHost,剩下的事情就交给WCF框架进行处理了。现在我们就来挖掘一下WCF框架是怎么完成这一工作的。^_^

一个服务去处理一个来自调用者的请求会经过4个步骤,我会在下面的章节中详细介绍这四个步骤:

  • 初始化
  • 开启服务
  • 接收连接
  • 处理请求

为了更好的理解我们将要讨论的,你需要有一些WCF的基础知识,并且使用过一段时间。现在我们开始吧。

初始化

在这一步,我们对于服务所有的描述,不论是通过配置文件,还是通过代码的方式,抑或是通过特性,都将会被转换成服务的ServiceDescription类型的实例,它收集了描述这个服务的所有的信息。这些信息用于服务的WSDL的生成,更重要的是,WCF会使用这些信息去创建一个运行时管道,通过这个运行时管道,WCF可以为客户端请求找到正确的服务实例和服务操作。

ServiceHost用来创建ServiceDescription,SeviceDescription的信息主要来源于编码和配置文件,所以为此,ServiceHost为每个来源提供了一个方法,CreateDescription和ApplyConfiguration。

在CreateDescription方法中,运用反射技术将在服务契约上定义的特性ServiceContract和OperationContract相关的信息给找出来,这些信息用于创建Behavior,ContractDescription,OperationDescription,如下图所示:

在ApplyConfiguration中相应的就从app.config配置文件中读取配置的信息,比如地址,绑定,契约,终结点,以及行为相关的配置(如果我们添加了的话)。

在创建好ServiceHost对象之后,我们还可以在代码中通过一些方法在这个ServiceHost开启之前对它进行一些修改,如下图所示:

最终,所有相关的信息都准备好放在了ServiceDescription中,现在我们可以使用这些信息去构建运行时管道来处理客户端请求了。恩,是时候开启这个服务了。

开启服务

我们对上面这张WCF运行时架构的图片应该不会陌生,客户端通过代理通过一系列的管道去访问服务端最终会被服务端的服务实例处理。在这个图片中,消息会被管道中所有的组件进行处理,但是我们并不知道这些组件是怎么创建的以及消息是怎么分发的。现在我就来解释一下当服务开启时这些组件是怎么创建的。

步骤1:ServiceHost会根据终结点配置信息,对每个监听地址创建一个ChannelListener,这个ChannelListener可以用于在之后创建服务端的信道和信道栈。

步骤2:构建ChannelDispatcher和EndpointDispatcher(根据监听地址和终结点地址),这两个组件在消息的分发中扮演着很重要的角色。

步骤3:构建ServiceDescription中定义的行为,这些行为可以影响到WCF运行时组件的行为。比如说,你在一个自定义行为中提供了一个自定义的InstanceProvider,当WCF运行时通过IInstanceProvider请求一个服务实例时,你可以用你自定义的InstanceProvider来提供。

步骤4:ChannelDispatcher打开ServiceHost创建的所有的ChannelListener,然后得到一个ImmutableDispatchRuntime 类型的实例,这个实例是用于分发消息的核心组件。当从信道中取出消息之后,ImmutableDispatchRuntime会拿到一个服务实例,然后选择操作去调用执行。

最后,信道分发器会创建一个Listenerhandle并使用ChannelPump方法来轮询接受客户端连接。

正如我们所见到的,在开启服务阶段我们还是在做一些准备的工作。我们设置了信道工厂,但是我们并没有创建信道栈。我们构建了所有的分发器,但是在消息到达之前他们是不会工作的。现在我们只需要一个消息来敲这一扇门。

接收连接

一旦ListenerHandler接收到了一个新连接,它就会调用ChannelListener的Accept方法创建信道。然后ListenerHandler创建一个ChannelHandler对象并与创建好的信道进行关联,然后ChannelHandler一直通过MesagePump对这个channel进行消息的轮询,换句话说,它创建了一个等待接受的通道。

处理请求

当客户端发出一个请求后,ChannelHandler会被告知有message进来通过一个回调。首先,他们通知所有的额channel去接收这个消息,这个时候所有的信道(尤其是协议信道)会有一个机会去处理这个消息来实现他们的协议,比如说Security,Transaction等。

然后这个ChannelHandler会根据优先级去调用所有的EndpointDispatcher的过滤方法来决定将这个消息分发到哪个Endpoint。然后使用EndpointDispatcher的DispatcherRuntime对象来决定去调用哪个服务操作。默认的,WCF跟根据消息请求头的Action属性来决定去选择哪个操作。在开启服务阶段,WCFhi创建一个DispatcherOperationRuntime字典,key为操作的Action属性(默认为操作名,可通过Name指定),所以如果我们传递一个消息的Action头给这个字典,我们就可以得到应该被调用的合适的操作。如果我们不想通过这样的额Action头去匹配,也可以使用自定义的OperationSelector。

在我们拿到DispatchOperationRuntime之后,ChannelJHandler还不能去调用它,因为我们还没有拿到响应的服务实例。所以ChannelHandler会分发消息到ImmutableDispatchRuntime去拿到一个服务实例上下文。首先它会check一下是否已经存在了一个上下文,如果有的话它就会尝试使用IInstanceContextProvider去拿到那个上下文。如果我们在服务行为上修饰InstanceContextMode为PerSession或Singleton,我们可能会拿到一个已经存在的实例上下文,而且我们之后拿到的服务实例也是同一个。否则,如果InstanceContextMode设置为PerCall的话,我们就必须总是要创建一个新的服务实例上下文了。在顺备好实例上下文之后ImmutableDispatchRuntime会调用IDispatchMessageInspector来拦截这个消息,在这个时候我们就可以自定义一个拦截器来围绕消息做一些事情,比如说打印消息内容、修改消息或者是进行消息的统计等等。消息在经过拦截器之后,ImmutableDispatchRuntime 会调用实例上下文来获得一个服务实例,随后实例上下文就调用InstanceProvider来拿到这个实例。现在你可以想象我们要在这里做什么了吧,既然我们拿到了服务实例,我们就可以去调用响应的方法。所以我们调用InvokeBegin方法在步骤1中查询到的DispatchOperationRuntime对象上。

现在我们开始去调用这个方法。首先我们需要反序列化这个消息来获得方法的CLR类型的参数,所以DispatchOperationRuntime 会调用IDispatcherMessageFormatter去做这件事情。在反序列化之后DispatchOperationRuntime会调用IParameterInspatcher去拦截这些参数。在这里你就有机会去对这些参数做一些手脚了,比如验证什么的。最后,我们使用IOperationInvoker来调用这个方法。

现在我们已经从迷宫中走出来了,我们已经知道了WCF运行时究竟做了什么。那么我们可以从中学到什么知识呢?个人认为,我从中学到了两件事:

  • 这个易用的编程模型是怎么工作的:

我学到了再我我的服务和操作加上一些WCF的特性后会发生什么,以及如果我改变其属性值会发生什么事情。

  • 怎样去扩展WCF运行时框架:

我学到了在WCF运行时框架中的一些可以扩展的地方,现在我知道了这些扩展会在什么时候被调用以及怎么被调用,所以我对于围绕他们做一些自定义的扩展很有信心。

最后,我想向大家展示一下WCF运行时的调用树。我想它应该是一个很复杂的协作图,但是我没有用协作图来表示是因为它太复杂了,没法再简单的word文档中呈现。所以我认为调用树应该会更合适,我希望它对于你一样也会有一些帮助。树中的颜色表明这个方法属于的class,@符号表示的是一些我自己命名的方法逻辑。树中可能会有一些错误,如果你发现错误的话请通知我一下,谢谢。

原文地址 http://www.cnblogs.com/idior/articles/971252.html

[翻译] WCF运行时架构的更多相关文章

  1. Fabric架构:抽象的逻辑架构与实际的运行时架构

    Fabric从1.X开始,在扩展性及安全性上面有了大大的提升,且新增了诸多的新特性: 多通道:支持多通道,提高隔离安全性. 可拔插的组件:支持共识组件.权限管理组件等可拔插功能. 账本数据可被存储为多 ...

  2. Flink 运行时架构

    参考链接:https://blog.csdn.net/dajiangtai007/article/details/88575553 1.Flink 运行时架构 Flink 运行时架构主要包含几个部分: ...

  3. ILBC 运行时 (ILBC Runtime) 架构

    本文是 VMBC / D# 项目 的 系列文章, 有关 VMBC / D# , 见 <我发起并创立了一个 VMBC 的 子项目 D#>(以下简称 <D#>)  https:// ...

  4. JVM运行时数据区域详解

    参考文章: <Java Se11 虚拟机规范> <深入理解Java虚拟机-JVM高级特性与最佳实践 第3版>- 周志明 本文基于Java Se 11讲解. 根据<Java ...

  5. WCF 框架运行时类图

    本文画出了 WCF 框架运行时的重点类之间的类关系图. Binding 一个 Binding 由多个 BindingElement 组成.BindingElement 作为主要的扩展点.每一个 Bin ...

  6. 所生成项目的处理器架构“MSIL”与引用“***”的处理器架构“x86”不匹配。这种不匹配可能会导致运行时失败。请考虑通过配置管理器...

    警告:所生成项目的处理器架构“MSIL”与引用“***”的处理器架构“x86”不匹配.这种不匹配可能会导致运行时失败.请考虑通过配置管理器更改您的项目的目标处理器架构,以使您的项目与引用间的处理器架构 ...

  7. permission 文档 翻译 运行时权限

    文档位置:API24/guide/topics/security/permissions.html  System Permissions 系统权限 Android is a privilege-se ...

  8. .Net 5中Windows Forms运行时的新功能(翻译)

    本文翻译自Igor的文章,原文地址:https://devblogs.microsoft.com/dotnet/whats-new-in-windows-forms-runtime-in-net-5- ...

  9. [翻译]Go与C#对比 第三篇:编译、运行时、类型系统、模块和其它的一切

    Go vs C#, Part 3: Compiler, Runtime, Type System, Modules, and Everything Else | by Alex Yakunin | S ...

随机推荐

  1. mac 文本编辑器 文本编码Unicode utf-8 不适用的问题

    在mac上使用默认的文本编辑器打开下载的xx.txt文件,如果文本是gbk的编码可能会出现 文本编码Unicode utf-8 不适用的打开错误,如下图 解决方式: 文本编辑---偏好设置-----打 ...

  2. sql注入基于错误-单引号-字符型

    查找注入点 在url中: 1. ' 2. and 1=1/and 1=2 3. 随即输入(整形) 4. -1/+1回显上下页面(整形) 5. and sleep(5) (判断页面返回时间)   判断有 ...

  3. 利用OVER开窗函数分页

    在SQL Server中,利用SQL进行分页的方法也有很多,今天要总结的是SQL Server 2005中引入的OVER开窗口函数,然后利用开窗函数进行分页. 示例代码如下: -- 设置数据库上下文 ...

  4. 使用Dottrace跟踪代码执行时间

    当自己程序遇到性能问题,比如IIs请求反应缓慢,客户端程序执行缓慢,怎么分析是哪里出了问题呢?dottrace可以帮助.net程序跟踪出代码里每个方法的执行时间,这样让我们更清晰的看出是哪里执行时间过 ...

  5. iOS开发UI篇—ios应用数据存储方式(XML属性列表-plist)

    iOS开发UI篇—ios应用数据存储方式(XML属性列表-plist) 一.ios应用常用的数据存储方式 1.plist(XML属性列表归档) 2.偏好设置 3.NSKeydeArchiver归档(存 ...

  6. Ubuntu 中文输入法安装包

    1. 打开 Dashboard http://www.2cto.com/os/201207/144189.html

  7. Node.js 常用工具 util

    util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足. util.inherits util.inherits(constructor ...

  8. KnockOut.js入门示例详解

    KnockOut框架简称KO,是微软将应用于WPF/Silverlight的MVVM模式在Web上的尝试,这是一个非常有用的JavaScript框架. KO的核心就是绑定,包括数据绑定和行为绑定: K ...

  9. ANT build.xml文件详解

    Ant的优点 跨平台性.Ant是用Java语言编写的,所示具有很好的跨平台性. 操作简单.Ant是由一个内置任务和可选任务组成的. Ant运行时需要一个XML文件(构建文件). Ant通过调用targ ...

  10. MySQL高可用之MHA搭建

    测试环境 节点1 172.16.200.231 6666               master         节点2 172.16.200.27 6666 slave1              ...