(一)ChannelFactory<T>的缓存机制
ChannelFactory<T>是一个复杂费时的工作,wcf内部使用缓存机制提高服务调用性能
CalculatorClient proxy1 = new CalculatorClient("calculatorservice");
proxy1.Open();
CalculatorClient proxy2 = new CalculatorClient("calculatorservice");
proxy2.Open();
Console.WriteLine(object.ReferenceEquals(proxy1.ChannelFactory, proxy2.ChannelFactory));
结果是true,证明两个ChannelFactory是同一个对象
破坏缓存机制的两种方式:
1。在构造函数中传入绑定的对象构建ClientBase<T>
2。在ClientBase<T>.Open之前,访问三个只读属性:ChannelFactory,Endpoint,ClientCredential
(二)基于会话信道的客户端
wcf的信道分为两种:
1。会话信道
它确保客户端与服务端之间消息能够相互关联,但是信道的错误(Fault)会影响后续的消息交换
2。数据报信道
即使同一数据报信道中,每次消息交互都是相互独立的,信道的错误也不会影响后续的消息交换
绝大部分绑定类型(BasicHttpBinding除外),默认情况下创建的都是会话信道。
(三)服务契约的关闭与并发会话的限制
maxConcurrentCalls
maxConcurrentInstances
maxConcurrentSessions =5
以上最大会话数量为5的情况下:
下面代码会报超时错误,但是要配合
<binding name="myWsHttpBind" sendTimeout="00:00:10"
receiveTimeout="00:00:10" openTimeout="00:00:10"/>
static void timeoutTest()
{
Binding binding = new WSHttpBinding();
EndpointAddress address = new EndpointAddress("http://127.0.0.1:9999/calculatorservice");
ChannelFactory<ICalculator> channelFactory =
new ChannelFactory<ICalculator>(binding, address);
for (int i = 1; i <= 20; i++)
{
try
{
ICalculator calculator = channelFactory.CreateChannel();
Console.WriteLine($"{i}:x+y={calculator.Add(1, 2)} when x={1} and y={2}");
}
catch (Exception ex)
{
Console.WriteLine($"{i}t:{ex.Message}");
}
}
Console.ReadKey();
}
解决这个问题,除了设置maxConcurrentSessions的大小,最根本的在于:
服务代理不用的情况下,应该及时关闭。这样基于服务代理对象的会话也会关闭。
上面谈的是最大会话限,对于非会话信道是否也有限制呢?
但是这个例子出错了:
12t:无法处理消息。这很可能是因为操作“http://www.artech.com/CalculatorService/Add”
不正确,或因为消息包含无效或过期的安 全上下文令牌,
或因为绑定之间出现不匹配。如果由于未处于活动状态导致服务中止了该通道,
则安全上下文令牌无效。若要防止服务永久中止闲置会话,请增加服务终结点绑定上的接收超时。
只要设置客户端的安全模式后,就会报错:
Binding binding = new WSHttpBinding(SecurityMode.None);
(四)会话信道与异常处理
调用除法方法时,如果除数为0,则服务端抛出DivideByZeroException异常,
当前信道的状态变成Faulted,表示信道错误。
错误的信道不能继续用于后续的通信,即使调用Close方法试图将期关闭也不行。
这种情况下会调用另一个方法:Abort,强行中断当前信道。
对于客户端来说,信道在下面两种情况下状态会变成Faulted
。调用超时,抛出TimeoutException
。调用失败,抛出CommunicationException
正确的处理服务调用代码应该是:
try
{
int result=calculator.Divide(1,0);
(calculator as ICommunicationObject).Close();
}
catch(CommunicationException ex)
{
(calculator as ICommunicationObject).Abort();
}
catch(TimeoutException ex)
{
(calculator as ICommunicationObject).Abort();
}
catch(Exception ex)
{
}
(五)通过AOP的方式解决会话信道的关闭与中断
性能与并发的权衡
由于信道的创建与释放都是一件相对耗时的操作,所以从性能上来讲,
我们鼓励服务代理对象(或者说是信道)的重用。
但是,服务代理重用就意味着信道一直保持开启的状态,
这样信道可能会达到服务端所允许的最大并发会话量,后续的请求就阻塞了,
所以从并发的角度,我们又不主张服务代理的重用。
这就是性能和可扩展性不可能同时兼得的问题。软件设计就是要在这两方面找一个平衡点。

