WCF 可扩展性
WCF 提供了许多扩展点供开发人员自定义运行时行为。 WCF 在 Channel Layer 之上还提供了一个高级运行时,主要是针对应用程序开发人员。在 WCF 文档中,它常被称为服务模型层(Service Model Layer)。该高级运行时主要由一个称作 Dispatcher(在 ServiceHost 的 Context 中)的组件和一个称作 Proxy(在客户端的 Context 中)的组件组成。
(图片引自 MSDN Magazine : Extending WCF with Custom Behaviors)
每个扩展点都使用接口定义来扩展。
Behavior 是一种特殊类型的类,它在 ServiceHost/ChannelFactory 初始化过程中扩展运行时行为。WCF 有四种类型的行为:服务行为、终结点行为、契约行为和操作行为。
每种行为类型也是通过不同的接口定义来扩展,它们都共用一组相同的方法。一个例外是,IServiceBehavior 没有 ApplyClientBehavior 方法,因为服务行为不能用于客户端。
WCF扩展点
(图片引自 lovecindywang = lovecherry 博客)
案例:使用WCF扩展记录服务调用时间
服务定义:
[ServiceContract] ICalculatorService { [OperationContract] b);6 }
服务实现:
CalculatorService : ICalculatorService { b) { b; }7 }
配置文件:
1 <system.serviceModel> 2 <services><span ><span <span <span /><span /> 8 <host> 9 <baseAddresses><span />11 </baseAddresses>12 </host>13 </service>14 </services>15 <behaviors>16 <serviceBehaviors>17 <behavior>18 <serviceMetadata/><span />20 </behavior>21 </serviceBehaviors>22 </behaviors>23 </system.serviceModel>
问题描述
现在需要记录每次服务调用执行所需的时间,比如可以将测量的方法执行时间传递给 PerformanceCounter 用于性能计数,或者直接写入到日志中等。
方式一:直接在方法内测量
CalculatorService : ICalculatorService { b) { Stopwatch.StartNew(); 6 b; waste time here 9 ,, watch.ElapsedMilliseconds));12 result; }15 }
这种方法浅显易懂,但如果服务所提供的功能过多,会导致大量冗余代码。
方式二:形成测量类简化代码
Measure : IDisposable {; 4 methodName) { methodName; Stopwatch(); }10 ; }12 Start() { _watch.Start(); }17 Stop() { _watch.Stop();21 , MethodName, _watch.ElapsedMilliseconds)); }25 methodName) { Measure(methodName); m.Start(); m; }32 Dispose() { Stop();; }38 }
View Code
CalculatorService : ICalculatorService { b) {)) { waste time here b; } }11 }
此种方式简化了代码,但仍然需要在各自方法内实现,并需提供方法名作为参数。
使用 Message Inspection 来解决问题
我们定义类 IncomingMessageLoggerInspector 来实现 IDispatchMessageInspector 接口。
#region IDispatchMessageInspector Members 2 AfterReceiveRequest( Message request, IClientChannel channel, InstanceContext instanceContext) { OperationContext.Current;;10 ParseOperationName(context.IncomingMessageHeaders.Action);12 MarkStartOfOperation( context.EndpointDispatcher.ContractName, operationName, context.SessionId); }17 correlationState) { OperationContext.Current;;22 ParseOperationName(context.IncomingMessageHeaders.Action);24 MarkEndOfOperation( context.EndpointDispatcher.ContractName, operationName, context.SessionId, correlationState); }29 #endregion
通过服务的当前上下文实例,我们可以获取到服务被调用的契约名称 ContractName,并且可以在 IncomingMessageHeaders 总解析出被调用的 OperationName。
我们在方法 MarkStartOfOperation 中启动 Stopwatch 开始测量执行时间,在方法执行完毕后服务模型会调用 BeforeSendReply 并将 Stopwatch 实例引用传递至 correlationState,此时我们可以在方法 MarkEndOfOperation 中解决时间测量,并打印日志。
#region Private Methods 2 action) { action; 6 action; 8 );) {); }14 actionName; }17 MarkStartOfOperation( sessionId) {.Format(CultureInfo.InvariantCulture,, operationName, inspectorType,, CultureInfo.InvariantCulture), Thread.CurrentThread.ManagedThreadId);26 Debug.WriteLine(message);28 Stopwatch.StartNew(); }31 MarkEndOfOperation( operationName, correlationState) { (Stopwatch)correlationState; watch.Stop();38 .Format(CultureInfo.InvariantCulture,, operationName, watch.ElapsedMilliseconds, inspectorType,, CultureInfo.InvariantCulture), Thread.CurrentThread.ManagedThreadId);44 Debug.WriteLine(message); }47 #endregion
DispatchRuntime 中。
IncomingMessageLoggerEndpointBehavior : IEndpointBehavior {#region IEndpointBehavior Members 4 AddBindingParameters( ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }10 ApplyClientBehavior( ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }16 ApplyDispatchBehavior( ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) {) { endpointDispatcher.DispatchRuntime.MessageInspectors.Add( IncomingMessageLoggerInspector()); } }27 Validate(ServiceEndpoint endpoint) { }31 #endregion33 }
然后,我们在 ServiceHost 实例化后,未Open前,将 IncomingMessageLoggerEndpointBehavior 添加至 Endpoint 的 Behaviors 中。
Program {[] args) {(CalculatorService)); 6 host.Description.Endpoints) { IncomingMessageLoggerEndpointBehavior()); }11 obj, EventArgs e) {); });16 host.Open();18 Console.ReadKey(); }21 }
使用 WcfTestClient.exe 调用服务,执行 2 + 3 查看结果,
在 Debug 输出中可以看到,
使用配置文件定制
使用配置文件定制扩展的优点就是可以按需添加和删除扩展,而无需改动代码。比如当发现系统有性能问题时,添加该扩展来查看具体哪个方法执行速度慢。
需要定义类来实现 BehaviorExtensionElement 抽象类。
IncomingMessageLoggerEndpointBehaviorExtension : BehaviorExtensionElement { Type BehaviorType {(IncomingMessageLoggerEndpointBehavior); } } 7 CreateBehavior() { IncomingMessageLoggerEndpointBehavior(); }12 }
在配置文件中添加扩展项,
>><span />>>
在终结点行为中添加该扩展定义,
>>>/>/>>>>>/>>>>
使用 ServiceBehavior 扩展
同理,如果服务实现了多个 Endpoint,则想在所有 Endpoint 上添加该扩展,除了可以逐个添加或者使用 behaviorConfiguration 来配置。
另一个方法是可以借助 IServiceBehavior 的扩展实现。
IncomingMessageLoggerServiceBehavior : IServiceBehavior {#region IServiceBehavior Members 4 AddBindingParameters( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, endpoints, BindingParameterCollection bindingParameters) { }12 ApplyDispatchBehavior( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {) { serviceHostBase.ChannelDispatchers) { channelDispatcher.Endpoints) { endpointDispatcher.DispatchRuntime.MessageInspectors.Add( IncomingMessageLoggerInspector()); } } } }29 Validate( ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }35 #endregion37 }
原理相同,仅是遍历通道分配器中所有的终结点,逐一添加 Inspector。
同理,如果需要在配置文件中使用,也需要实现一个 BehaviorExtensionElement 类。
IncomingMessageLoggerServiceBehaviorExtension : BehaviorExtensionElement { Type BehaviorType {(IncomingMessageLoggerServiceBehavior); } } 7 CreateBehavior() { IncomingMessageLoggerServiceBehavior(); }12 }
共同学习,写下你的评论
评论加载中...
作者其他优质文章