WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
原文:WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service Proxy)。而ChannelFactory<T>则是服务代理的创建者。WCF采用基于终结点(Endpoint)服务消费方式:WCF服务通过一个或者多个终结点暴露给潜在的服务消费者,服务的消费中通过与之匹配的终结点与之交互。在客户端,我们具有两种典型的服务代理创建方式,其一是通过诸如SvcUtil.exe这样的工具导入服务的元数据生成相应的服务代理(一个继承自ClientBase<T>的类型)代码和相关配置;其二是直接通过相应的终结点信息(通过代码指定或者配置)创建ChannelFactory<T>对象,并借助该对象直接进行服务代理的创建。
实际上,即使通过ClientBase<T>对象进行服务调用,其内部也是调用ChannelFactory<T>创建的服务代理。整个ChannelFactory<T>的创建是一项相对复杂并且费时的工作,会涉及很多诸如反射、配置文件的读取等操作。为了提高服务调用的性能,在.NET 3.5中,WCF在ClientBase<T>中引入了ChannelFactory<T>的缓存机制。
一、如何实现对ChannelFactory<T>的缓存
为了让读者对ChannelFactory<T>的缓存机制有一个直观的认识,我们来做一个简单的实验:在一个Console应用中执行如下的代码,其中CalculatorClient可以看成是本节开篇时自定义的服务代理类。在本例中,先后以相同的方式(调用相同的构造函数,传入相同的参数)创建并开启了两个CalculatorClient对象,然后检验它们的ChannelFactory是否是相同的对象。
1: CalculatorClient proxy1 = new CalculatorClient("calculateservice");
2: proxy1.Open();
3: CalculatorClient proxy2 = new CalculatorClient("calculateservice");
4: proxy2.Open();Console.WriteLine("object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory) = {0}",
5: object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory));
输出结果:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory) = True
从输出的结果,可以看出两个不同的ClientBase<T>对象使用了相同的ChannelFactory<T>对象。这得益于在.NET 3.5中新加入的ChannelFactory<T>的缓存机制。那么,在WCF客户端框架内部对ChannelFactory<T>的缓存是如何实现的呢?
实际上,ChannelFactory<T>的缓存实现很简单,被创建出来的ChannelFactory<T>集合通过ClientBase<T>的一个静态变量保存起来。我们可以将这个ChannelFactory<T>集合看成是一个字典,字典的值就是ChannelFactory<T>,而键则通过下面三个对象派生:
- CallbackInstance:以InstanceContext对象表示的对回调对象的封装;
- EndpointConfigurationName:终结点在配制文件中的名称;
- RemoteAddress:终结点的远程地址,类型为EndpointAddress。
它们分别与ClienBase<T>构造函数中相应的参数相匹配。当调用某个构造函数创建对象的时候,WCF将传入的三个参数作为Key(如果再构造函数中并未指定相应的参数,会使用默认值,EndpointConfigurationName、CallbackInstance和RemoteAddress的默认值分别为*、null和null),从缓存(静态变量)中去找匹配的ChannelFactory<T>对象,如果成功找到,则直接返回,否则重新创建,在返回之前将其放入缓存中。
从这个意义上讲,多个ClienBase<T>对象能够重用相同的ChannelFactory<T>对象的前提是它们使用相同的构造函数,并传入相同的参数被创建。为了验证这一点,再来做一个实验,只须要将上面的例子稍加修改,通过另一个重载构造函数来创建CalculatorClient对象。
1: CalculatorClient proxy1 = new CalculatorClient("calculateservice",new EndpointAddress("http://127.0.0.1:9999/calculateservice");
2: proxy1.Open();
3:
4: CalculatorClient proxy2 = new CalculatorClient("calculateservice");
5: proxy2.Open();
6: Console.WriteLine("object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory) = {0}",
7: object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory));
8:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
输出结果:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory) = False
实际上,proxy1和proxy2最终使用的终结点地址是相同的(http://127.0.0.1:9999/ calculatorservice),只不过一个是通过代码指定的,另一个则是通过配置文件配置的。但是,就是因为创建ClienBase<T>时使用了不同的构造函数重载,导致不能重用同一个ChannelFactory<T>对象。
ChannelFactory<T>的重用避免了频繁地常见ChannelFactory<T>对象,从而获得更好的性能。在具体的应用中,我们应该尽可能地利用这样的机制。但是,由于编程人员对ChannelFactory<T>的缓存机制不了解,不知不觉就会使这个缓存机制失效。接下来就来讨论这个问题。
二、ChannelFactory<T>缓存机制的失效
总体来讲,下面的两种情况会引起ChannelFactory<T>缓存机制失效。
- 在构造函数中传入绑定对象构建ClientBase<T>;
- 在ClientBase<T>开启(调用Open方法)之前,访问如下三个只读属性:ChannelFactory、Endpoint和ClientCredential。
为了加深读者的理解,我们通过实验的方式来证实上面的两种说法。为了验证在构造函数中传入绑定对象对ChannelFactory<T>缓存机制的影响,写了如下的代码:通过Binding和EndpointAddress对象创建ClienBase<T>对象。
1: Binding binding = new BasicHttpBinding();
2: EndpointAddress address = new EndpointAddress("http://127.0.0.1:9999/calculateservice");
3: CalculatorClient proxy1 = new CalculatorClient(binding,address);
4: proxy1.Open();
5: CalculatorClient proxy2 = new CalculatorClient(binding, address);
6: proxy2.Open();
7: Console.WriteLine("object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory) = {0}",
8: object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory));
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
输出结果:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory) = False
接下来,再通过实验整个在ClientBase<T>开启(调用Open方法)之前访问ChannelFactory、Endpoint和ClientCredential三个只读属性对ChannelFactory<T>缓存机制的影响。在这里,以访问ChannelFactory属性为例
1: CalculatorClient proxy1 = new CalculatorClient("calculateservice");
2: ChannelFactory<ICalculator> factory = proxy1.ChannelFactory;
3: proxy1.Open();
4: CalculatorClient proxy2 = new CalculatorClient("calculateservice");
5: proxy2.Open();
6: Console.WriteLine("object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory) = {0}",
7: object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory));
8:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
输出结果:
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
1: object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory) = False
在上面的例子中,在Proxy1的Open方法调用之前,调用了只读属性ChannelFactory,并将其赋值到一个临时变量中,中间根本没有对ChannelFactory<T>作任何修改,仅仅一次我们认为微不足道的对只读属性的访问就破坏了WCF客户端框架对ChannelFactory<T>的缓存机制。
三、如何有效利用ChannelFactory<T>的缓存机制
为了能够充分利用ChannelFactory<T>的缓存机制,获得更好的服务调用性能,我们可以得出以下两个最佳实践:
- 避免通过人为指定绑定对象创建ClientBase<T>对象,应该尽可能使用配置的绑定信息;
- 避免在ClientBase<T>开启之前读取ChannelFactory、Endpoint和ClientCredential三个属性,或者在创建ClientBase<T>之后显式调用Open方法开启ClientBase<T>对象。
注:部分内容节选自《WCF技术剖析(卷1)》第八章:客户端(Clients)
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制的更多相关文章
- WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...
- 《WCF技术剖析》博文系列汇总[持续更新中]
原文:<WCF技术剖析>博文系列汇总[持续更新中] 近半年以来,一直忙于我的第一本WCF专著<WCF技术剖析(卷1)>的写作,一直无暇管理自己的Blog.在<WCF技术剖 ...
- WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)
原文:WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在.NE ...
- WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成
原文:WCF技术剖析之七:如何实现WCF与EnterLib PIAB.Unity之间的集成 在这之前,我写过深入介绍MS EnterLib PIAB的文章(参阅<MS Enterprise Li ...
- WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇]
原文:WCF技术剖析之三十:一个很有用的WCF调用编程技巧[下篇] 在<上篇>中,我通过使用Delegate的方式解决了服务调用过程中的异常处理以及对服务代理的关闭.对于<WCF技术 ...
- WCF技术剖析之十一:异步操作在WCF中的应用(上篇)
原文:WCF技术剖析之十一:异步操作在WCF中的应用(上篇) 按照操作执行所需的资源类型,我们可以将操作分为CPU绑定型(CPU Bound)操作和I/O绑定型(I/O Bound)操作.对于前者,操 ...
- WCF技术剖析之十一:异步操作在WCF中的应用(下篇)
原文:WCF技术剖析之十一:异步操作在WCF中的应用(下篇) 说完了客户端的异步服务调用(参阅WCF技术剖析之十一:异步操作在WCF中的应用(上篇)),我们在来谈谈服务端如何通过异步的方式为服务提供实 ...
- WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?
原文:WCF技术剖析之二十: 服务在WCF体系中是如何被描述的? 任何一个程序都需要运行于一个确定的进程中,进程是一个容器,其中包含程序实例运行所需的资源.同理,一个WCF服务的监听与执行同样需要通过 ...
- WCF技术剖析之十三:序列化过程中的已知类型(Known Type)
原文:WCF技术剖析之十三:序列化过程中的已知类型(Known Type) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话) ...
随机推荐
- poj 1715 Hexadecimal Numbers 排列组合
/** 大意: 给定16进制数的16个字母,,求第k大的数,,要求数的长度最大为8.,并且每个数互不相同. 思路: 从高到低挨个枚举,每一位能组成的排列数 ,拿最高位来说,能做成的排列数为15*A(1 ...
- [Android]Dalvik的BOOTCLASSPATH和dexopt流程
BOOTCLASSPATH简介1.BOOTCLASSPATH是Android Linux的一个环境变量,可以在adb shell下用$BOOTCLASSPATH看到.2.BOOTCLASSPATH于/ ...
- 下载的firebug-lite压缩包的调用方法
把以下代码copy到地址栏按回车,等加载完毕之后(受网速限制,有时候等待会久一点,如果很久都无法加载,重试几次就ok了),就会显示firebug lite的窗口,这样不用更改页面任何东西,任何地方轻松 ...
- 腾讯QQ是用什么语言开发的(转)
腾讯QQ的部分COM组件用的VC6,用exescope看其中几个dll的依赖,依赖于MFC42.dll,MSVCRT.dll,MSVCP60.dll都说明是VC6写的. 还有一部分用的VS2005,包 ...
- eclipse 部分颜色及部分字体设置
eclipse整体代码的颜色风格可以用插件 eclipse color theme 更改. 但尽管如此,有些颜色仍不是最满意的,还需自己设计. 1. 光标选中字体的颜色,如图 一个openItem被选 ...
- Java面试题之三
十一.谈谈final,finally,finalize的区别? 1.final:是修饰符,是一个关键字.修饰变量,如果是基本类型表示该变量的值不能修改:如果是引用类型表示该变量不能指向别的对象:修饰类 ...
- 国外稳定的免费PHP空间byethost.com
byethost.com是一个老牌的免费空间商,从2006年起就開始提供免费空间了,其免费服务很稳定(看完下文你就知道有多稳定了). 提供5.5G的免费空间,200G的月流量,能够绑定50个域名,也能 ...
- 模拟Hibernate框架的小demo
该程序为尚学堂马士兵老师讲解,模拟了hibernate的原理,主要应用了字符串拼接,反射知识. step1,新建数据库 use jd; create table _student( _id int(1 ...
- asp.net利用剪切板导出excel
public enum ClipboardFormats : uint { CF_TEXT = 1, CF_BITMAP = 2, CF_METAFILEPICT = 3, CF_SYLK = 4, ...
- git阶段学习总结
学习git大约有两个星期了,脑子里总算有点干货了,可以拿出来总结一下: git,用于版本控制的,刚开始觉得它是linux下默认的命令,其实也是个工具需要apt-get install git 安装一下 ...