最后一章将进行WCF扩展和新特性的学习,这部分内容有一定深度,有一个基本的了解即可,当需要自定义一个完整的SOA框架时,可以再进行细致的学习和实践。

服务端架构体系的构建主要包含接下来的几个要素:服务描述、终结点分发器选择机制、信道分发器、终结点分发器、分发运行时、分发操作。

ServiceDescription服务描述包含一组终结点和服务行为列表,而终结点包含服务地址、绑定和契约信息,契约中则包含操作和契约行为,操作信息中包含操作行为

终结点分发器具有两个消息筛选器,一个是地址筛选器,另一个是契约筛选器,均继承自MessageFilter,信道分发器通过他们来选择某个终结点分发器来接受请求消息,此外还有一个FilterPriority来决定顺序。在WCF中,终结点ServiceEndpoint和其分发器EndpointDispatcher一一对应。

信道分发器ChannelDispatcher包含信息监听器、错误处理器、进行流量控制的服务限流器和信道初始化器,这部分可以扩展的属性包括异常细节信息的传播、手工寻址、最大挂起消息数、同步/异步消息接受和事物控制。

分发运行时DispatcherRuntime是整个WCF服务端运行的核心,其包含很多扩展组件,例如关于安全的ServiceAuthenticationManager、ServiceAuthorizationManager、RoleProvider和AuthorizationPolicy,与服务激活相关的实例提供者、上下文提供者、单例实例上下文和实例上下文初始化器,与并发控制有关的同步上下文,可以对接受的请求消息和回复消息进行相应操作的消息检验器,运行时操作列表和用于选择操作的选择器,自己在项目构建中很多时候可以参考这样的方式。此外,还包括以下的扩展属性,授权、安全审核、事务和会话、未处理的操作、SOAP报头验证、并发控制。

分发操作是在Servicehostk开启后,有OperationDescription转化来的,附加在其上的可扩展组件包括调用上下文初始化器CallContextInitializer,参数检验器ParameterInspector,用于消息序列化和反序列化的消息格式化器DispatchMessageFormatter,用于操作方法的执行的操作调用器OperationInvoker。

客户端架构体系的构建则包含如下几个要素:创建ChannelFactory<TChannel>、客户端运行时、客户端操作、服务代理和服务调用。在通过调用构造函数创建一个ChannelFactory<TChannel>对象后,WCF会根据指定的终结点创建一个ServiceEndpoint对象,其整个结构如下图示。

通过定义四种行为对WCF扩展,这4中行为分别是服务行为、终结点行为、契约行为和操作行为,它们具有相同的4个方法Validate、AddBindingParameters、ApplyDispatchBehavior和ApplyClientBehavior。原书还包含一个关于本地化的扩展例子,需要时可以查阅,注意Properties中的资源文件。

ServiceHost对WCF的扩展一般是通过继承自ServiceHost的自定义类来实现,但需要注意,对Description、分发运行时的定制是无效的,此外可以通过自定义类来集成Unity。

简化开发体验内容包括:默认终结点(标准终结点)、默认绑定配置、默认行为配置和无.svc文件服务激活等。这部分默认的配置就不一一介绍了,唯一需要介绍的就是WCF提供的标准终结点,如下表所示。

标准终结点 描述
mexEndpoint 用于公开服务元数据的标准终结点
dynamicEndpoint 使用WS-Discovery在运行时动态查找终结点地址的标准终结点
discoveryEndpoint 发送/接收发现消息的标准终结点
UdpDiscoveryEndpoint 通过UDP多播方式发送/接收发现消息的标准终结点
announcementEndpoint 由服务用于发送公告消息的标准终结点
udpAnnouncementEndpoint 由服务用于通过UDP绑定发送公告消息的标准终结点
workflowControlEndpoint 可用于对工作流实例调用控制操作的标准终结点
webHttpEndpoint 带有自动添加webHttpBehavior行为的WebHttpBinding绑定的标准终结点
webScriptEndpoint 带有自动添加webScriptEnablingBehavior行为的WebHttpBinding绑定的标准终结点

此外,无.svc文件服务的配置如下所示。

 <system.serviceModel>
<serviceHostingEnvironment>
<serviceActivations>
<add service="Sory.CoreFramework.Service.EmployeeService" relativeAddress="EmployeeService.svc"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>

路由服务实际上就是一个WCF服务,当端到端通信而不是点对点通信非常有用,可以将请求转发。RoutingService包括4个服务契约接口,ISimplexDatagramRouter、ISimplexSessionRouter、IRequestReplyRouter和IDuplexSessionRouter,实际上是同ProcessRequest/ProcessMessage两个方法来工作,此外其OperationBehaviorAttribute特性的Impersonation设置为Allowed即允许身份模拟。其路由策略涉及如下几个要素,RoutingBehavior服务行为、消息筛选器和筛选器表。

服务发现

之前的内容都是服务提供方和需求方直接沟通场景,那么现在有一个问题,当企业应用很多,需要统一管理服务建立SOA体系时,如何发现服务。这儿就涉及WS-Discovery服务发现的知识了,其包含两种基本的操作模式,Ad-Hoc和Managed。前者客户端在一定的网络范围内以广播的形式发送探测Probe消息搜索目标服务,在该探测消息中,包含相应的搜寻条件,不过感觉管理性很差,不推荐。后者Managed模式通过维护一个所有可用目标服务的中心发现代理(中介者模式),客户端只需要将探测消息发给该中心,即可得到目标服务信息,接下来着重介绍Managed模式。

Managed模式,可用服务都注册在发现代理中,其服务发现过程如下图所示,和想象中的基本一致,服务是去中心的,但服务发现即其目录是中心化的。

接下来通过蒋大师的一个自定义的发现代理服务来彻底了解一个SOA治理中心基本构建方法,其基本步骤包括:创建自定义发现代理服务;寄宿发现代理服务和目标服务;服务的动态调用。

 服务端代码
public class DiscoveryAsyncResult:IAsyncResult
{
public object AsyncState{ get; private set; }
public WaitHandle AsyncWaitHandle { get; private set; }
public bool CompletedSynchronously { get; private set; }
public bool IsCompleted { get; private set; }
public EndpointDiscoveryMetadata Endpoint { get; private set; } public DiscoveryAsyncResult(AsyncCallback callback, object asyncState)
{
this.AsyncState = asyncState;
this.AsyncWaitHandle = new ManualResetEvent(true);
this.CompletedSynchronously = this.IsCompleted = true;
if(callback != null)
{
callback(this);
}
} public DiscoveryAsyncResult(AsyncCallback callback, object asyncState, EndpointDiscoveryMetadata endpoint)
:this(callback, asyncState)
{
this.Endpoint = endpoint;
}
} [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class DiscoveryProxyService : DiscoveryProxy
{
public IDictionary<EndpointAddress, EndpointDiscoveryMetadata> Endpoints { get; private set; } public DiscoveryProxyService()
{
this.Endpoints = new Dictionary<EndpointAddress, EndpointDiscoveryMetadata>();
} /// <summary>
/// Find(Probe)
/// </summary>
protected override IAsyncResult OnBeginFind(FindRequestContext findRequestContext, AsyncCallback callback, object state)
{
var endpoints = from item in this.Endpoints
where findRequestContext.Criteria.IsMatch(item.Value)
select item.Value;
foreach (var endpoint in endpoints)
{
findRequestContext.AddMatchingEndpoint(endpoint);
}
return new DiscoveryAsyncResult(callback, state);
} protected override void OnEndFind(IAsyncResult result) { }
/// <summary>
/// Resolve
/// </summary>
protected override IAsyncResult OnBeginResolve(ResolveCriteria resolveCriteria, AsyncCallback callback, object state)
{
EndpointDiscoveryMetadata endpoint = null;
if (this.Endpoints.ContainsKey(resolveCriteria.Address))
{
endpoint = this.Endpoints[resolveCriteria.Address];
}
return new DiscoveryAsyncResult(callback, state);
} protected override EndpointDiscoveryMetadata OnEndResolve(IAsyncResult result)
{
return ((DiscoveryAsyncResult)result).Endpoint;
} /// <summary>
/// Online
/// </summary>
protected override IAsyncResult OnBeginOnlineAnnouncement(DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata endpointDiscoveryMetadata, AsyncCallback callback, object state)
{
this.Endpoints[endpointDiscoveryMetadata.Address] = endpointDiscoveryMetadata;
return new DiscoveryAsyncResult(callback, state);
} protected override void OnEndOnlineAnnouncement(IAsyncResult result) { } protected override IAsyncResult OnBeginOfflineAnnouncement(DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata endpointDiscoveryMetadata, AsyncCallback callback, object state)
{
if (this.Endpoints.ContainsKey(endpointDiscoveryMetadata.Address))
{
this.Endpoints.Remove(endpointDiscoveryMetadata.Address);
}
return new DiscoveryAsyncResult(callback, state);
} protected override void OnEndOfflineAnnouncement(IAsyncResult result) { }
} public static void Start()
{
using (var discoveryProxyService = new ServiceHost(typeof(DiscoveryProxyService)))
using(var employeeService = new ServiceHost(typeof(EmployeesService)))
{
discoveryProxyService.Open();
employeeService.Open();
}
} 服务端配置
<system.serviceModel>
<services>
<service name ="Sory.CoreFramework.Service.DiscoveryProxyService">
<endpoint address="net.tcp://127.0.0.1:8866/discoveryproxy/probe" binding="netTcpBinding" kind="discoveryEndpoint" isSystemEndpoint="false" ></endpoint>
<endpoint address="net.tcp://127.0.0.1:8867/discoveryproxy/announcement" binding="netTcpBinding" kind="announcementEndpoint"></endpoint>
</service>
<service name ="Sory.CoreFramework.Service.EmployeesService" behaviorConfiguration="serviceAnnouncement">
<endpoint address="http://127.0.0.1:3721/employees" binding="ws2007HttpBinding"
contract="Sory.CoreFramework.Interface.IEmployees"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceDiscovery/>
</behavior>
<behavior name="serviceAnnouncement">
<serviceDiscovery>
<announcementEndpoints>
<endpoint kind="announcementEndpoint" address="net.tcp://127.0.0.1:8867/discoveryproxy/announcement" binding="netTcpBinding"></endpoint>
</announcementEndpoints>
</serviceDiscovery>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel> 客户端代码
using (ChannelFactory<IEmployees> channelFactory = new ChannelFactory<IEmployees>("employeeService"))
{
var proxy = channelFactory.CreateChannel();
Array.ForEach<Employee>(proxy.GetAll().ToArray(), emp => Console.WriteLine(emp));
} 客户端配置
<system.serviceModel>
<client>
<endpoint name="employeeService" kind="dynamicEndpoint" endpointConfiguration="unicastEndpoint"
binding="ws2007HttpBinding" contract="Sory.CoreFramework.Interface.IEmployees">
</endpoint>
</client>
<standardEndpoints>
<dynamicEndpoint>
<standardEndpoint name="unicastEndpoint">
<discoveryClientSettings>
<endpoint kind="discoveryEndpoint" address="net.tcp://127.0.0.1:8888/discoveryproxy/probe" binding="netTcpBinding"></endpoint>
</discoveryClientSettings>
</standardEndpoint>
</dynamicEndpoint>
</standardEndpoints>
</system.serviceModel>

终于完成WCF最后一块拼图,非常的开心,虽然赶脚只掌握了3成左右,但也基本足够了,需要时再回顾学习了。

参考资料:

[1]蒋金楠. WCF全面解析[M]. 上海:电子工业出版社, 2012.

快速入门系列--WCF--08扩展与新特性的更多相关文章

  1. vue 快速入门 系列 —— vue loader 扩展

    其他章节请看: vue 快速入门 系列 vue loader 扩展 在vue loader一文中,我们学会了从零搭建一个简单的,用于单文件组件开发的脚手架.本篇将在此基础上继续引入一些常用的库:vue ...

  2. 快速入门系列--WebAPI--01基础

    ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...

  3. 快速入门系列--WebAPI--04在老版本MVC4下的调整

    WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了.在之前的介绍中,基本上都基于.NET 4.5之后版本,其System.N ...

  4. 快速入门系列--MVC--01概述

    虽然使用MVC已经不少年,相关技术的学习进行了多次,但是很多技术思路的理解其实都不够深入.其实就在MVC框架中有很多设计模式和设计思路的体现,例如DependencyResolver类就包含我们常见的 ...

  5. [转]快速入门系列--WebAPI--01基础

    本文转自:http://www.cnblogs.com/wanliwang01/p/aspnet_webapi_base01.html ASP.NET MVC和WebAPI已经是.NET Web部分的 ...

  6. 快速入门系列--MVC--07与HTML5移动开发的结合

    现在移动互联网的盛行,跨平台并兼容不同设备的HTML5越来越盛行,很多公司都在将自己过去的非HTML5网站应用渐进式的转化为HTML5应用,使得一套代码可以兼容不同的物理终端设备和浏览器,极大的提高了 ...

  7. webpack 快速入门 系列 - 自定义 wepack 上

    其他章节请看: webpack 快速入门 系列 自定义 wepack 上 通过"初步认识webpack"和"实战一"这 2 篇文章,我们已经学习了 webpac ...

  8. vue 快速入门 系列 —— vue loader 下

    其他章节请看: vue 快速入门 系列 vue loader 下 CSS Modules CSS Modules 是一个流行的,用于模块化和组合 CSS 的系统.vue-loader 提供了与 CSS ...

  9. webpack 快速入门 系列 —— 性能

    其他章节请看: webpack 快速入门 系列 性能 本篇主要介绍 webpack 中的一些常用性能,包括热模块替换.source map.oneOf.缓存.tree shaking.代码分割.懒加载 ...

随机推荐

  1. Python操控mysql数据库

    导入库——MySQLdb 1.链接MySQL conn=MySQL.connect(host=',port=3306) 此处主机用ip地址,不能使用'localhost',不然会显示链接错误. 2.选 ...

  2. 『TCP/IP详解——卷一:协议』读书笔记——14

    2013-08-25 11:32:06 第5章 RARP:逆地址解析协议 5.1 引言 具有本地磁盘的系统引导时,一般是从磁盘上的配置文件中读取IP地址.但是无盘机,如X终端或无盘工作站,则需要采用其 ...

  3. vs2013的安装以及单元测试

    一.安装过程 1.下载vs2013安装包,打开进行安装.安装过程时间有点长,大概用了一个小时. 2.安装完成.需要登录,可以选择以后再说. 3.选择颜色主题. 4.打开vs2013的界面. 5.添加密 ...

  4. 游戏Loading中的小提示和Loading动画实现

    学习unity1年多了,工作也1年了,因为工作需要,有几个月没接触unity Ngui啦. 学的还是不踏实.继续努力吧.由于下周就要进行新游戏的开发,这几天熟悉熟悉NGUI,今天按照现在公司以前的项目 ...

  5. 百度音乐api

    百度音乐全接口 会利用使用接口找歌简单又快捷 http://tingapi.ting.baidu.com/v1/restserver/ting 获取方式:GET 参数:format=json或xml& ...

  6. mottoes

    1. You don't kown if you can until a try. 2. Rule youself. 3. It's what you do in the dark that puts ...

  7. 在WPF的WebBrowser控件中屏蔽脚本错误的提示

    在WPF中使用WebBrowser控件显示网页时,经常会报脚本错误的提示,如何屏蔽掉这些错误提示呢.方法是定义如下方法: public void SuppressScriptErrors(WebBro ...

  8. explode,split,preg_split性能比较

      explode,split,preg_split性能比较 分类: php2012-07-12 09:46 1109人阅读 评论(1) 收藏 举报 三个函数都是用来对字符串进行分割,下面分几个实验来 ...

  9. 一个事务复制的bug--更新丢失 续

    阅读本文之前请参考http://www.cnblogs.com/stswordman/p/3258897.html 最近又做了一个case,环境是sql server 2008 R2. 客户添加了一个 ...

  10. 【腾讯bugly干货】QQ空间直播秒开优化实践

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址为:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1204&am ...