WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制

简介: 原文:WCF技术剖析之八:ClientBase中对ChannelFactory的缓存机制和传统的分布式远程调用一样,WCF的服务调用借助于服务代理(Service Proxy)。而ChannelFactory则是服务代理的创建者。
原文: 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));

输出结果:

   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:  

输出结果:

   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));

输出结果:

   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:  

输出结果:

   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)

WCF技术剖析系列:

WCF技术剖析之一:通过一个ASP.NET程序模拟WCF基础架构
WCF技术剖析之二:再谈IIS与ASP.NET管道
WCF技术剖析之三:如何进行基于非HTTP的IIS服务寄宿
WCF技术剖析之四:基于IIS的WCF服务寄宿(Hosting)实现揭秘
WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务
WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效
WCF技术剖析之七:如何实现WCF与EnterLib PIAB、Unity之间的集成
WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
WCF技术剖析之九:服务代理不能得到及时关闭会有什么后果?
WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理

WCF技术剖析之十一:异步操作在WCF中的应用(上篇)
WCF技术剖析之十一:异步操作在WCF中的应用(下篇)
WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)
WCF技术剖析之十三:序列化过程中的已知类型(Known Type)
WCF技术剖析之十四:泛型数据契约和集合数据契约(上篇)
WCF技术剖析之十四:泛型数据契约和集合数据契约(下篇)
WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用
WCF技术剖析之十六:数据契约的等效性和版本控制


作者: Artech
出处: http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
目录
相关文章
|
3月前
|
存储 缓存 前端开发
HTTP的缓存机制是什么?
HTTP的缓存机制是什么?
28 1
|
3月前
|
存储 消息中间件 缓存
redis的缓存机制
redis的缓存机制
92 0
|
10天前
|
XML 缓存 Java
MyBatis二级缓存解密:深入探究缓存机制与应用场景
MyBatis二级缓存解密:深入探究缓存机制与应用场景
46 2
MyBatis二级缓存解密:深入探究缓存机制与应用场景
|
1月前
|
存储 缓存 算法
深入探究LRU缓存机制:优化内存利用与提升性能
深入探究LRU缓存机制:优化内存利用与提升性能
128 1
|
1月前
|
存储 缓存 Java
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
|
3月前
|
Shell Unix Linux
Linux 终端命令之文件浏览(3) less
Linux 终端命令之文件浏览(3) less
30 0
Linux 终端命令之文件浏览(3) less
|
3月前
|
Java Go C++
Java每日一练(20230427) 螺旋矩阵、LRU缓存机制、解数独
Java每日一练(20230427) 螺旋矩阵、LRU缓存机制、解数独
26 0
Java每日一练(20230427) 螺旋矩阵、LRU缓存机制、解数独
|
3月前
|
Python 缓存 机器学习/深度学习
Python每日一练(20230331) 阶乘后的零、无重复最长子串、LRU缓存机制
Python每日一练(20230331) 阶乘后的零、无重复最长子串、LRU缓存机制
33 0
Python每日一练(20230331) 阶乘后的零、无重复最长子串、LRU缓存机制
|
3月前
|
缓存 关系型数据库 MySQL
MySQL高效运行的秘密:BufferPool缓存机制深度剖析!
MySQL高效运行的秘密:BufferPool缓存机制深度剖析!
MySQL高效运行的秘密:BufferPool缓存机制深度剖析!
|
4月前
|
缓存 前端开发
前端知识笔记(三十六)———HTTP 缓存机制
前端知识笔记(三十六)———HTTP 缓存机制
25 0