快速入门系列--WCF--08扩展与新特性
最后一章将进行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文件服务的配置如下所示。
1 <system.serviceModel>
2 <serviceHostingEnvironment>
3 <serviceActivations>
4 <add service="Sory.CoreFramework.Service.EmployeeService" relativeAddress="EmployeeService.svc"/>
5 </serviceActivations>
6 </serviceHostingEnvironment>
7 </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治理中心基本构建方法,其基本步骤包括:创建自定义发现代理服务;寄宿发现代理服务和目标服务;服务的动态调用。
1 服务端代码
2 public class DiscoveryAsyncResult:IAsyncResult
3 {
4 public object AsyncState{ get; private set; }
5 public WaitHandle AsyncWaitHandle { get; private set; }
6 public bool CompletedSynchronously { get; private set; }
7 public bool IsCompleted { get; private set; }
8 public EndpointDiscoveryMetadata Endpoint { get; private set; }
9
10 public DiscoveryAsyncResult(AsyncCallback callback, object asyncState)
11 {
12 this.AsyncState = asyncState;
13 this.AsyncWaitHandle = new ManualResetEvent(true);
14 this.CompletedSynchronously = this.IsCompleted = true;
15 if(callback != null)
16 {
17 callback(this);
18 }
19 }
20
21 public DiscoveryAsyncResult(AsyncCallback callback, object asyncState, EndpointDiscoveryMetadata endpoint)
22 :this(callback, asyncState)
23 {
24 this.Endpoint = endpoint;
25 }
26 }
27
28 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
29 public class DiscoveryProxyService : DiscoveryProxy
30 {
31 public IDictionary<EndpointAddress, EndpointDiscoveryMetadata> Endpoints { get; private set; }
32
33 public DiscoveryProxyService()
34 {
35 this.Endpoints = new Dictionary<EndpointAddress, EndpointDiscoveryMetadata>();
36 }
37
38 /// <summary>
39 /// Find(Probe)
40 /// </summary>
41 protected override IAsyncResult OnBeginFind(FindRequestContext findRequestContext, AsyncCallback callback, object state)
42 {
43 var endpoints = from item in this.Endpoints
44 where findRequestContext.Criteria.IsMatch(item.Value)
45 select item.Value;
46 foreach (var endpoint in endpoints)
47 {
48 findRequestContext.AddMatchingEndpoint(endpoint);
49 }
50 return new DiscoveryAsyncResult(callback, state);
51 }
52
53 protected override void OnEndFind(IAsyncResult result) { }
54 /// <summary>
55 /// Resolve
56 /// </summary>
57 protected override IAsyncResult OnBeginResolve(ResolveCriteria resolveCriteria, AsyncCallback callback, object state)
58 {
59 EndpointDiscoveryMetadata endpoint = null;
60 if (this.Endpoints.ContainsKey(resolveCriteria.Address))
61 {
62 endpoint = this.Endpoints[resolveCriteria.Address];
63 }
64 return new DiscoveryAsyncResult(callback, state);
65 }
66
67 protected override EndpointDiscoveryMetadata OnEndResolve(IAsyncResult result)
68 {
69 return ((DiscoveryAsyncResult)result).Endpoint;
70 }
71
72 /// <summary>
73 /// Online
74 /// </summary>
75 protected override IAsyncResult OnBeginOnlineAnnouncement(DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata endpointDiscoveryMetadata, AsyncCallback callback, object state)
76 {
77 this.Endpoints[endpointDiscoveryMetadata.Address] = endpointDiscoveryMetadata;
78 return new DiscoveryAsyncResult(callback, state);
79 }
80
81 protected override void OnEndOnlineAnnouncement(IAsyncResult result) { }
82
83 protected override IAsyncResult OnBeginOfflineAnnouncement(DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata endpointDiscoveryMetadata, AsyncCallback callback, object state)
84 {
85 if (this.Endpoints.ContainsKey(endpointDiscoveryMetadata.Address))
86 {
87 this.Endpoints.Remove(endpointDiscoveryMetadata.Address);
88 }
89 return new DiscoveryAsyncResult(callback, state);
90 }
91
92 protected override void OnEndOfflineAnnouncement(IAsyncResult result) { }
93 }
94
95 public static void Start()
96 {
97 using (var discoveryProxyService = new ServiceHost(typeof(DiscoveryProxyService)))
98 using(var employeeService = new ServiceHost(typeof(EmployeesService)))
99 {
100 discoveryProxyService.Open();
101 employeeService.Open();
102 }
103 }
104
105 服务端配置
106 <system.serviceModel>
107 <services>
108 <service name ="Sory.CoreFramework.Service.DiscoveryProxyService">
109 <endpoint address="net.tcp://127.0.0.1:8866/discoveryproxy/probe" binding="netTcpBinding" kind="discoveryEndpoint" isSystemEndpoint="false" ></endpoint>
110 <endpoint address="net.tcp://127.0.0.1:8867/discoveryproxy/announcement" binding="netTcpBinding" kind="announcementEndpoint"></endpoint>
111 </service>
112 <service name ="Sory.CoreFramework.Service.EmployeesService" behaviorConfiguration="serviceAnnouncement">
113 <endpoint address="http://127.0.0.1:3721/employees" binding="ws2007HttpBinding"
114 contract="Sory.CoreFramework.Interface.IEmployees"/>
115 </service>
116 </services>
117 <behaviors>
118 <serviceBehaviors>
119 <behavior>
120 <serviceDiscovery/>
121 </behavior>
122 <behavior name="serviceAnnouncement">
123 <serviceDiscovery>
124 <announcementEndpoints>
125 <endpoint kind="announcementEndpoint" address="net.tcp://127.0.0.1:8867/discoveryproxy/announcement" binding="netTcpBinding"></endpoint>
126 </announcementEndpoints>
127 </serviceDiscovery>
128 </behavior>
129 </serviceBehaviors>
130 </behaviors>
131 </system.serviceModel>
132
133 客户端代码
134 using (ChannelFactory<IEmployees> channelFactory = new ChannelFactory<IEmployees>("employeeService"))
135 {
136 var proxy = channelFactory.CreateChannel();
137 Array.ForEach<Employee>(proxy.GetAll().ToArray(), emp => Console.WriteLine(emp));
138 }
139
140 客户端配置
141 <system.serviceModel>
142 <client>
143 <endpoint name="employeeService" kind="dynamicEndpoint" endpointConfiguration="unicastEndpoint"
144 binding="ws2007HttpBinding" contract="Sory.CoreFramework.Interface.IEmployees">
145 </endpoint>
146 </client>
147 <standardEndpoints>
148 <dynamicEndpoint>
149 <standardEndpoint name="unicastEndpoint">
150 <discoveryClientSettings>
151 <endpoint kind="discoveryEndpoint" address="net.tcp://127.0.0.1:8888/discoveryproxy/probe" binding="netTcpBinding"></endpoint>
152 </discoveryClientSettings>
153 </standardEndpoint>
154 </dynamicEndpoint>
155 </standardEndpoints>
156 </system.serviceModel>
终于完成WCF最后一块拼图,非常的开心,虽然赶脚只掌握了3成左右,但也基本足够了,需要时再回顾学习了。
参考资料:
[1]蒋金楠. WCF全面解析[M]. 上海:电子工业出版社, 2012.
- “盲”逆向:iOS 应用 Blind 寻踪
- 根据时间字段导入数据的问题总结 (r6笔记第6天)
- Gnuboard 漏洞分析
- 一次数据库宕机问题的分析(r6笔记第5天)
- PWN学习之house of系列(一)
- 清理session的小插曲(二) (r6笔记第4天)
- 3.训练模型之在GPU上训练的环境安装
- 深度学习对话系统实战篇 -- 简单 chatbot 代码实现
- pangrank算法--PageRank算法并行实现
- 刷爆朋友圈的 deepfakes 视频人物换脸是怎样炼成的?
- 干货 | ElasticSearch相关性打分机制
- FCN 的简单实现
- 2.运行一个demo
- ROWNUMBER() OVER( PARTITION BY COL1 ORDER BY COL2)/ ROWNUMBER() OVER( PARTITION BY COL1 ORDER BY CO
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python 库的安装及使用 常见错误异常及解决办法
- Kafka三种可视化监控管理工具Monitor/Manager/Eagle
- 超性感的React Hooks(五):自定义hooks
- 三、变量对象
- Java编程 经验技巧汇总
- 关于IP地址的一些相关知识点
- vmware的三种网络模式
- 我的 Chrome 版本不支持生成二维码,30 分钟怒怼了一个插件,附源码
- Android开发(第一行代码 第二版) 常见异常和解决办法(基于Android Studio)(二)
- 大厂Java项目如何进行Maven多模块管理
- Android开发 经验技巧汇总(基于Android Studio)(一)
- 老板逼我用 Git,本地指令介绍
- Python全栈(三)数据库优化之5.MySQL自关联、外键与Python操作MySQL
- Android开发 经验技巧汇总(基于Android Studio)(二)
- 表格滑动和图片链接,mdnice安排上了!