[翻译] WCF运行时架构
原文地址 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运行时架构的更多相关文章
- Fabric架构:抽象的逻辑架构与实际的运行时架构
Fabric从1.X开始,在扩展性及安全性上面有了大大的提升,且新增了诸多的新特性: 多通道:支持多通道,提高隔离安全性. 可拔插的组件:支持共识组件.权限管理组件等可拔插功能. 账本数据可被存储为多 ...
- Flink 运行时架构
参考链接:https://blog.csdn.net/dajiangtai007/article/details/88575553 1.Flink 运行时架构 Flink 运行时架构主要包含几个部分: ...
- ILBC 运行时 (ILBC Runtime) 架构
本文是 VMBC / D# 项目 的 系列文章, 有关 VMBC / D# , 见 <我发起并创立了一个 VMBC 的 子项目 D#>(以下简称 <D#>) https:// ...
- JVM运行时数据区域详解
参考文章: <Java Se11 虚拟机规范> <深入理解Java虚拟机-JVM高级特性与最佳实践 第3版>- 周志明 本文基于Java Se 11讲解. 根据<Java ...
- WCF 框架运行时类图
本文画出了 WCF 框架运行时的重点类之间的类关系图. Binding 一个 Binding 由多个 BindingElement 组成.BindingElement 作为主要的扩展点.每一个 Bin ...
- 所生成项目的处理器架构“MSIL”与引用“***”的处理器架构“x86”不匹配。这种不匹配可能会导致运行时失败。请考虑通过配置管理器...
警告:所生成项目的处理器架构“MSIL”与引用“***”的处理器架构“x86”不匹配.这种不匹配可能会导致运行时失败.请考虑通过配置管理器更改您的项目的目标处理器架构,以使您的项目与引用间的处理器架构 ...
- permission 文档 翻译 运行时权限
文档位置:API24/guide/topics/security/permissions.html System Permissions 系统权限 Android is a privilege-se ...
- .Net 5中Windows Forms运行时的新功能(翻译)
本文翻译自Igor的文章,原文地址:https://devblogs.microsoft.com/dotnet/whats-new-in-windows-forms-runtime-in-net-5- ...
- [翻译]Go与C#对比 第三篇:编译、运行时、类型系统、模块和其它的一切
Go vs C#, Part 3: Compiler, Runtime, Type System, Modules, and Everything Else | by Alex Yakunin | S ...
随机推荐
- C/C++-----------http协议发送字段,文件,单个和多张图片
关于c/c++ 网络编程,无论在linux还是windows,要说到自由性,和安全性,socket无疑是比较好的!对于socket,因为它的传输协议只有两种tcp和udp,属于网络层,这里我们不去重点 ...
- app——分享wap站,数据处理页面展示
无意中接到了一个小的工作任务:配合手机app端的分享功能做一个wap站,简言之:将手机app端分享的文章id传过来,利用此id再进行一系列的操作,由于文章分为纯文本,图文以及图集的三种类型的文章,因此 ...
- [整]C#获取天气预报信息(baidu api)包括pm2.5
/// <summary> /// 获取天气预报信息 /// </summary> /// <returns></returns> public Bai ...
- NetworkComms V3 之支持TCP连接和UDP连接
NetworkComms V3 无缝的支持TCP连接和UDP连接. 您可以很容易的创建这两种连接 //创建一个连接信息对象 ConnectionInfo connInfo = ); //创建一个TCP ...
- iOS开发拓展篇—UIDynamic(简单介绍)
iOS开发拓展篇—UIDynamic(简单介绍) 一.简单介绍 1.什么是UIDynamic UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架 可以认为是一种物理引擎,能模拟 ...
- SQL语句---nvl 用法
SQL语句---nvl 用法 一NVL函数是一个空值转换函数 NVL(表达式1,表达式2) 如果表达式1为空值,NVL返回值为表达式2的值,否则返回表达式1的值. 该函数的目的是把一个空值(nul ...
- 一些iOS心得
ARC 1,arc是什么? automatic referece counting mrc mannualiOS5 之后出来的技术// 2,arc的原理是什么?// 在程序编译的时候,系统帮我 ...
- eclipse真机调试显示Target unknown的解决方法
eclipse的android模拟器调试是个硬伤,非常非常卡,严重影响工作效率.个人推荐使用第三方模拟器Genymotion,不了解的可以搜索下安装.你会发现它真心好用,但需要官网注册的. 各种调试完 ...
- android 第一个程序的编写
移通152余继彪 需求分析:输入两个数字,让他们相乘,然后得出结果 首先建立一个android项目 在 layout中建立第一个界面 该界面有四个组件,两个editText 一个TextView,一个 ...
- MVC乱码可能的原因
1.数据传输不对,或者根本没有进控制器 2.分部视图建立时一定要选择