为了账号安全,请及时绑定邮箱和手机立即绑定

使用WCF扩展记录服务调用时间

标签:
架构

WCF 可扩展性

WCF 提供了许多扩展点供开发人员自定义运行时行为。 WCF 在 Channel Layer 之上还提供了一个高级运行时,主要是针对应用程序开发人员。在 WCF 文档中,它常被称为服务模型层(Service Model Layer)。该高级运行时主要由一个称作 Dispatcher(在 ServiceHost 的 Context 中)的组件和一个称作 Proxy(在客户端的 Context 中)的组件组成。

(图片引自 MSDN Magazine : Extending WCF with Custom Behaviors)

每个扩展点都使用接口定义来扩展。

StageInterceptor InterfaceDescription
Parameter InspectionIParameterInspectorCalled before and after invocation to inspect and modify parameter values.
Message FormattingIDispatchMessageFormatter IClientFormatterCalled to perform serialization and deserialization.
Message InspectionIDispatchMessageInspector IClientMessageInspectorCalled before send or after receive to inspect and replace message contents.
Operation SelectionIDispatchOperationSelector IClientOperationSelectorCalled to select the operation to invoke for the given message.
Operation InvokerIOperationInvokerCalled to invoke the operation.

Behavior 是一种特殊类型的类,它在 ServiceHost/ChannelFactory 初始化过程中扩展运行时行为。WCF 有四种类型的行为:服务行为、终结点行为、契约行为和操作行为。

ScopeInterfacePotential Impact
  ServiceEndpointContractOperation
ServiceIServiceBehavior
EndpointIEndpointBehavior 
ContractIContractBehavior  
OperationIOperationBehavior   

每种行为类型也是通过不同的接口定义来扩展,它们都共用一组相同的方法。一个例外是,IServiceBehavior 没有 ApplyClientBehavior 方法,因为服务行为不能用于客户端。

MethodDescription
ValidateCalled just before the runtime is built—allows you to perform custom validation on the service description.
AddBindingParametersCalled in the first step of building the runtime, before the underlying channel is constructed, allows you to add parameters to influence the underlying channel stack.
ApplyClientBehaviorAllows behavior to inject proxy (client) extensions. Note that this method is not present on IServiceBehavior.
ApplyDispatchBehaviorAllows behavior to inject dispatcher extensions.

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   }


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号

举报

0/150
提交
取消