介绍

在.NET 3.0 SP1(与.NET 3.5一起发布) 中,WCF客户端创建有一个重要的性能改进。对BasicHttpBinding 来说,性能已经接近于创建ASMX代理。

ASMX 代理 vs WCF 代理

ASMX 代理比WCF代理更简单。前者是类型System.Web.Services.Protocols.SoapHttpClientProtocol的一个包装。在ASMX世界中,编程模型是两条平行线:

1) 没有ServiceContract的概念。客户端代理中的所有服务操作(WebMethod)的签名都是唯一的。客户端直接通过SoapHttpClientProcotol.Invoke来调用那些操作。所以在服务接口和底层消息处理系统上没有差别。

2) 没有明确的信道栈。SoapHttpClientProtocol在客户端包装了所有东西。它直接调用System.Net API HttpWebRequest.

相反WCF提供一种“垂直的”编程模型。你可以在客户端得到像服务端一样丰富的编程特性。WCF内部代理是一个包装在类型System.ServiceModel.Channels.ServiceChannel内部的.NET远程传输代理。传输代理允许你执行必要的类型转换并让你像使用ServiceContract接口那样使用代理调用服务操作。

创建WCF 代理的代价

正如我上篇博客指出的那样,有两种创建WCF客户端代理(信道)的常用方法:

A 使用WCF默认支持的ClientBase<T>,你可以使用svcutil.exe来生成客户端代理代码。

B 使用ChannelFactory<T>.CreateChannel().

ClientBase<T>类型化代理的常用模式是在每次调用过程中执行创建/释放资源:

    // You may add try/catch block here ...CalculatorClient clientProxy = new CalculatorClient();clientProxy.Open();// Make a call with the proxyclientProxy.Add(1, 2);clientProxy.Close();// Error handling code here ..

这里CalculatorClient是通过svcutil.exe自动生成的而且是ClientBase<ICalculator>的一个子类。

在.NET 3.0 中,创建/释放WCF客户端代理都是一个非常昂贵的操作。比ASMX代理要差很多。方案A在内部创建ChannelFactory<T>.每个代理都保留一个ChannelFactory作为私有字段。ChannelFactory的生命周期完全由代理控制。方案B 能稍微好一些因为你可以自己控制ChannelFactory<T>对象并能够实现只创建一次从而节省进一步创建代理的消耗。

为什么创建/释放ChannelFactory会如何昂贵?这是因为它涉及了以下几个主要操作:

1) 构造ContractDescription树

2) 反射所有需要的CLR类型

3) 构造信道栈

4) 释放所有资源

大多数情况下,这些数据在以相同方式创建的不同代理之间是相同的。

在.NET 3.0 SP1(与.NET 3.5 一起发布),在这部分有两个主要的性能改进:

1) ClientBase<T>内部对ChannelFactory进行缓存。这显著改进了方案A的性能。

2) 改进了信道管理逻辑并使方案A 和 B都能受益。

通过这些改进,创建WCF代理的性能与ASMX有相当可比性。

改进

改进ClientBase<T>性能的思路很简单:对ChannelFactory对象进行缓存。这与ASMX代理中的客户端类型缓存类似。

为ClientBase<T>进行的ChannelFactory缓存。

核心思想是在AppDomain层缓存ChannelFactory对象, 这样一个ChannelFactory对象的生存时间就不由代理控制了。缓存是一种最常使用(most recently used, MRU)缓存。缓存大小被硬编码为32以便于将最少使用的ChannelFactory对象从缓存中移出。

由于有了ChannelFactory 缓存,创建一个ClientBase<T>对象的粗略过程如下:

1) 在ClientBase<T>构造函数中,会有一个lookup 方法在缓存中寻找一个匹配的ChannelFactory.

2) 如果找到了,对这个ChannelFactory的引用数量会加1. 否则,会基于设置新创建一个ChannelFactory.

3) 在ClientBase<T>的内部信道(传输代理)创建之前,如果其他公共属性(比如ChannelFactory, Endpoint和ClientCredentials)被访问过,那么当前ClientBase<T>的缓存逻辑会被禁用。

4) 一旦内部信道被成功创建且ClientBase<T>的ChannelFactory对象没有被从缓存中去除而且缓存没有被禁用,ChannelFactory对象会被添加到缓存中。

什么是匹配的ChannelFactory?

我之前说过”匹配的“ChannelFactory了吗?是的,你得保证你可以基于ClientBase<T>构造函数中的输入设置来区分不同的ChannelFactory对象。有两种类型构造函数:将绑定作为参数的和不将绑定作为参数的。这里是那些不将绑定作为参数的:

ClientBase();
ClientBase(string endpointConfigurationName);
ClientBase(string endpointConfigurationName, string remoteAddress);
ClientBase(string endpointConfigurationName, EndpointAddress remoteAddress);
ClientBase(InstanceContext callbackInstance);
ClientBase(InstanceContext callbackInstance, string endpointConfigurationName);
ClientBase(InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress);
ClientBase(InstanceContext callbackInstance, string endpointConfigurationName, EndpointAddress remoteAddress);

对这些构造函数来说,所有的参数(包括默认的那个)都在下面列表中:

InstanceContext callbackInstance
string endpointConfigurationName
EndpointAddress remoteAddress

只要在构造ClientBase<T>的时候这些参数是一样的,我们可以完全假设会使用同样的ChannelFactory。幸运的是,String和EndpointAddress类型是不变的, 比如,我们可以做些简单的比较来确定这两个参数是否一样。对InstanceContext来说,我们可以使用对象引用比较。EndpointTrait<TChannel>类型作为MRU缓存的键。

这里是以绑定作为参数的两个构造函数:

ClientBase(Binding binding, EndpointAddress remoteAddress);
ClientBase(InstanceContext callbackInstance, Binding binding, EndpointAddress remoteAddress);

既然绑定对象是不变的,比如,人们可以在一个代理上使用它,然后修改一下并在另外一个代理上使用,我们可以很容易地检查一个绑定对象是否有改动。因此当绑定被提供给构造函数时,对应代理的缓存机制被禁用。

禁用缓存

WCF支持当ChannelFactory被打开之前修改ChannelFactory和ContractDescription设置的扩展形式。对一个新创建的代理做如下改动会阻止它与其他代理共享同样的信道。ClientBase<T>的其他属性也是同样的逻辑:

ChannelFactory
Endpoint
ClientCredentials

当内部信道被创建且ClientBase<T>.Open被调用以后任何对ClientBase<T>属性的访问都会导致对应代理的缓存被禁用。这是很自然的,因为你也不想让一个代理重用为其他代理设置的不同契约或者安全设置的ChannelFactory。

信道管理

每个内部创建的信道都会被添加到System.ServiceModel.Channels.CommunicationObjectManager. 从这个类中添加/删除一个元素的性能已经在.NET 3.5 中改进。这是通过内部将List<T>换成Hashtable 实现的。这也是为什么方案2会有性能改进。

最佳实践

现在我得谈论一下内部实现了,我想总结一下如何使用WCF代理来实现最佳性能。

重用同样的代理

在很多情况,你会想重用同样的代理。因为这样会极大地提升性能。尤其是当你使用安全特性而对安全协议进行初始化很耗资源的情况下。

不要忘记在我在上一篇博客中提到的再使用代理之前要显式打开它。

使用代理来开启缓存

正如上面提到的,你可以使用ChannelFactory<T>.CreateChannel来创建代理或者使用自动生成的代理。如果你使用后者,你需要注意以下几点以使ChannelFactory被系统缓存:

1) 不要使用代理中将绑定作为参数的构造函数,你可以将绑定信息放到配置文件中去。

2) 不要访问代理的ChannelFactory、Endpoint 和 ClientCredentials的公共属性。

禁用缓存

如果你真的想禁用缓存,你可以简单地通过违反以上条件来实现。在一些场景,由于下面两个原因可能导致你不想让所有线程都使用同样的代理:

1) 信道的上下文不允许多线程访问。

2) 当使用一个单一代理时信道栈可能会有一些影响性能的瓶颈。

缓存的逻辑可以是非常简单,也可以是非常复杂。总之要记住以下几点:

1) 你需要为管理代理而实现正确的同步逻辑。

2) 你需要保证使用同样的代理。有时候,你可能想要为代理实现一个循环利用模式。

3) 你需要处理异常并重建缓存。

4) 缓存大小需要限制并且可以配置。

5) 你可能需要创建代理即便缓存池中没有可以使用的代理。

原文: http://blogs.msdn.com/b/wenlong/archive/2007/10/27/performance-improvement-of-wcf-client-proxy-creation-and-best-practices.aspx

.NET 3.5 中WCF客户端代理性能改进以及最佳实践相关推荐

  1. C4C Cloud Application Studio做ABSL开发的一些性能方面的最佳实践

    Stefan Hagen在博文SAP Cloud Application Studio Performance Best Practices里介绍了在C4C里使用Cloud Application S ...

  2. Qt 5.12 LTS(长期维护版本)中Qt Quick的性能改进

    我们一直致力于提高Qt的性能和优化其内存消耗.Qt 5.12的一个重点关注是在于减少QML引擎的内存消耗和优化JavaScript性能. 与上一个长期支持版Qt 5.6 LTS相比,Qt 5.9 LT ...

  3. .NET 5 中的正则引擎性能改进(翻译)

    前言 System.Text.RegularExpressions 命名空间已经在 .NET 中使用了多年,一直追溯到 .NET Framework 1.1.它在 .NET 实施本身的数百个位置中使用 ...

  4. 一文了解 NextJS 并对性能优化做出最佳实践

    点击上方 前端Q,关注公众号 回复加群,加入前端Q技术交流群 引言- 从本文中,我将从是什么,为什么,怎么做来为大家阐述 NextJS 以及如何优化 NextJS 应用体验. 一.NextJS是什么- ...

  5. Lustre I/O性能特点与最佳实践

    1 Lustre概述Lustre是面向集群的存储架构,它是基于Linux平台的开源集群(并行)文件系统,提供与POSIX兼容的文件系统接口.Lustre两个最大特征是高扩展性和高性能,能够支持数万客户 ...

  6. java中字符和字节的转换_Java最佳实践–字符到字节和字节到字符的转换

    java中字符和字节的转换 在使用Java编程语言时,我们将继续讨论与建议的实践有关的系列文章,我们将讨论String性能调优. 特别是,我们将着重于使用默认编码时如何有效地处理字符到字节和字节到字符 ...

  7. 在SQL Server中配置索引创建内存设置的最佳实践

    介绍 (Introduction) The Index Create Memory setting is an advanced SQL Server configuration setting wh ...

  8. WCF 客户端代理生成 通过SvcUtil.exe

    WCF服务调用通过两种常用的方式:一种是借助代码生成工具SvcUtil.exe或者添加服务引用的方式,一种是通过ChannelFactory直接创建服务代理对象进行服务调用. 下面简单说下如何通过Sv ...

  9. 京东微信购物性能优化,最佳实践总结!

    京东微信购物首页(以下简称微信首页)曾经作为微信购物一级入口(目前替换为京喜小程序)一直对性能有着极高的要求,本文将介绍微信首页的一些优化经验. 一般来说产品是按以下方式进行迭代的,我认为循环的起点应 ...

最新文章

  1. 测试用html文件是否存在,ASP如何检测某文件夹是否存在,不存在则自动创建
  2. 最简单的方式实现一棵二叉树
  3. Day5---D4:合规和审计管理
  4. Opencv——DFT变换(实现两个Mat的卷积以及显示Mat的频域图像)
  5. sass 自定义函数
  6. java基础知识5-二进制
  7. 苹果发布高端头戴式耳机AirPods Max12月15日发售
  8. Android中sendMessageAtTime()的用法
  9. samurai_ii__vengeance(武士二:复仇)无法在android上运行的原因
  10. 基于vue开发一个组件库
  11. 【笔记】WGS84转GCj02
  12. 国庆集训1101+1103(未完成)
  13. 泰坦尼克号分析是否获救
  14. 百度竞价账户关键词怎么拓词
  15. P8462 「REOI-1」奶油蛋糕
  16. 盘一盘 Python 特别篇 18 - 时区|夏令时
  17. vue延迟渲染组件_Vue 动态组件渲染问题分析
  18. google的视频下载插件
  19. 办公网建设设备选型及报价参考(500人规模)
  20. 【日常问题】chrome开启无痕模式,屏蔽第三方cookie

热门文章

  1. 不仅仅是手机,MWC现全球首例 5G NR 商用部署
  2. 并发-9-Callable和Future
  3. 关于zipfile解压出现的字符编码问题
  4. linux 文件编码问题
  5. JavaScript 的同源策略
  6. FreeSWITCH IVR中lua调用并执行nodejs代码
  7. linux卸载mysql,apache,php
  8. FPA笔记三 数据功能的识别
  9. vuex之state-状态对象的获取方法(三)
  10. DAG添加第二台主机报超时