[翻译] 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 ...
随机推荐
- zabbix3.0.4 部署之六 (zabbix3.0.4安装)
1. 新建zabbix用户,新建mysql zabbix数据库,并授权. groupadd zabbix #创建用户组zabbix useradd zabbix -g zabbix -s /bin/f ...
- 详解应对平台高并发的分布式调度框架TBSchedule
转载: 详解应对平台高并发的分布式调度框架TBSchedule
- VMWARE + CENTOS在windows下配置cocos2d-x android开发环境
VMWARE + CENTOS在windows配置cocos2d-x android开发环境 之前使用cygwin在windows开发android,后来使用了c++11特性,在cygwin中更新工具 ...
- 读javascript高级程序设计11-事件
一.事件流 事件流指从页面中接收事件的顺序. 1.事件冒泡(常用) IE中采用的事件流是事件冒泡,先从具体的接收元素,然后逐步向上传播到不具体的元素. 2.事件捕获(少用) Netscapte采用事件 ...
- HDU 1106 排序 题解
排序 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submissi ...
- [转]如何让div中的内容垂直居中
转自:http://blog.163.com/yan_1990/blog/static/197805107201211311515454/ 虽然Div布局已经基本上取代了表格布局,但表格布局和Div布 ...
- 9patch边框黑线的含义
上面黑线或者点表示纵向可拉伸的区域 一般一个点即可 左边黑线或者点表示横向可拉伸的区域 一般一个点即可 下面表示纵向填放内容的区域 一般是一条黑线 右边表示横向填放内容的区域 一般是一条黑线
- 【小月博客】用HTML5的File API做上传图片预览功能
前段时间做了一个项目,涉及到上传本地图片以及预览的功能,正好之前了解过 html5(点击查看更多关于web前端的有关资源) 可以上传本地图片,然后再网上看了一些demo结合自己的需求,终于搞定了.(P ...
- C#微信开发-微信JS-SDK(1)之通过config接口注入权限验证配置
官方文档是微信JS-SDK的使用步骤http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html#JSSDK.E4.BD.B ...
- (BFS)poj1465-Multiple
题目地址 题意可理解为我们有一些给定的元素,要用它们组成数,如果一个长度(x)所有组成的数都不是给定的另一个数(n)的倍数,并且长度为x的数中有模n的不同于长度小于x的数模n的数,那么继续延长这个数的 ...