本篇文章介绍可以算是WCF 4.0基于限流(Throttling)的新特性,是在修订《WCF技术剖析(卷1)》的时候编写演示实例的时候发现的。这个特性没有出现在官方文档上面,至少在MSDN上的相关介绍依然是错误的。
一、流量限制简介
WCF是一个基于多线程的消息监听、接收和处理框架体系,能够同时应付来自相同或者不同客户端的服务调用请求,并提供完善的同步机制确保状态的一致性。一方面,我们期望WCF服务端能够处理尽可能多的并发请求,但是资源的有限性决定了并发量有一个最大值。如果WCF不控制进入消息处理系统的并发量,试图处理所有抵达的并发请求,一旦超过了这个临界值,整个服务端将会由于资源耗尽而崩溃。
所以,我们需要在WCF的消息接收系统和消息处理系统之间设置一道道屏障,将流入消息处理系统的请求控制到一个最佳的范围,以实现对现有资源的有效利用,从而达到确保服务的可用性和提高整体吞吐量的目的。WCF的流向限制(Throttling)为你设置了这些屏障,你可以根据现有的软硬件环境对该闸门准入的并发流量进行动态的配置。
WCF对限流的控制是通过一个服务行为(Service Behavior)实现的,该服务行为类型名称为ServiceThrottlingBehavior,定义在System.ServiceModel.Description命名空间下。ServiceThrottlingBehavior定了三个整型的属性:MaxConcurrentCalls、MaxConcurrentInstances和MaxConcurrentSessions。它们分别代表流量控制的三个阀值,简单地说,我们所说的限流就是通过设置这三个值控制能够处理的并发量。
二、MSDN关于三个限流阀值的介绍
基于.NET Framework 4.0的MSDN对上述三个限流阀值是这样介绍的:
MaxConcurrentCalls:获取或设置一个值,该值指定整个 ServiceHost 中正在处理的最多消息数,默认值为 16;
MaxConcurrentInstances:获取或设置一个值,该值指定服务中可以一次执行的最多 InstanceContext 对象数,默认值为 26;
MaxConcurrentSessions:获取或设置一个指定 ServiceHost 对象可一次接受的最多会话数的值,默认值为 10。
三、通过实例测试默认的最大并发会话数
通过ServiceThrottlingBehavior的MaxConcurrentSessions属性表示的最大并发会话数默认为10,果真如此吗?我们不妨通过一个简单的实例来验证。照理以计算服务为例,下面是契约接口和服务类型的定义。
1: [ServiceContract(Namespace ="http://www.artech.com/")]
2: public interface ICalculator
3: {
4: [OperationContract]
5: double Add(double x, double y);
6: }
7:
8: public class CalculatorService : ICalculator
9: {
10: public double Add(double x, double y)
11: {
12: return x + y;
13: }
14: }
我们采用控制台程序对CalculatorService进行寄宿,如下所示的是采用的配置:
1: <configuration>
2: <system.serviceModel>
3: <services>
4: <service name="Artech.WcfServices.Service.CalculatorService">
5: <endpoint address="http://127.0.0.1:3721/calculatorservice"
6: binding="ws2007HttpBinding"
7: contract="Artech.WcfServices.Service.Interface.ICalculator"/>
8: </service>
9: </services>
10: </system.serviceModel>
11: </configuration>
从上面的配置中我们知道寄宿的服务具有一个唯一的基于WS2007HttpBinding(支持会话)的终结点。客户端采用相应的配置并通过如下的代码进行服务的调用。
1: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
2: {
3: bool stop = false;
4: for (int i = 0; i < 1000 && !stop; i++)
5: {
6: ICalculator calcultor = channelFactory.CreateChannel();
7: try
8: {
9: calcultor.Add(1, 2);
10: Console.WriteLine("第{0}个服务代理调用成功!", i + 1);
11: }
12: catch (Exception ex)
13: {
14: Console.WriteLine("出现异常:{0}", ex.Message);
15: stop = true;
16: }
17: }
18: }
在上面这段用于进行服务调用的代码中,我们通过基于客户端终结点配置名称创建的ChannelFactory<TChannel>对象创建了1000个服务代理进行用其进行1000次服务调用。当上面这个实例运行的时候,客户端控制台将会出现如下的输出结果。实例程序清晰地反映了这样的事实:虽然我们通过不同的服务代理对象进行了1000次服务调用,但是只有前面两百次是成功的。如果默认的最大并发会话数是10的话,只有前面10次服务调用会成功。
1: 第1个服务代理调用成功!
2: 第2个服务代理调用成功!
3: ...
4: 第199个服务代理调用成功!
5: 第200个服务代理调用成功!
6: 出现异常:请求通道在等待 00:00:59.9844000 以后答复时超时。增加传递给请求调用的超时值,或者增加绑定上的 SendTimeout 值。分配给此操作的时间可能已经是更长超时的一部分。
四、WCF 4.0中三个限流默认阀值具体是多少呢?
通过上面演示的实例,我们发现默认情况下允许200次并发会话,那么MaxConcurrentSessions的默认值不是10,而是200吗?由于三个限流属性值是通过配置的方式进行指定的,所以要了解它们的默认值,只需要了解对应的配置元素类型的定义即可。下面是ServiceThrottlingBehavior对应的配置元素ServiceThrottlingElement 的定义。
1: public sealed class ServiceThrottlingElement : BehaviorExtensionElement
2: {
3: //...
4: [ConfigurationProperty("maxConcurrentCalls", DefaultValue=0x10)]
5: public int MaxConcurrentCalls { get; set; }
6:
7: [ConfigurationProperty("maxConcurrentInstances", DefaultValue=0x74)]
8: public int MaxConcurrentInstances { get; set; }
9:
10: [ConfigurationProperty("maxConcurrentSessions", DefaultValue=100)]
11: public int MaxConcurrentSessions { get; set; }
12: }
13:
从应用在三个配置属性上ConfigurationPropertyAttribute特性可以看出,MaxConcurrentCalls、MaxConcurrentInstances 和MaxConcurrentSessions 的默认值为16、116和100,而不是MSDN所说的16、26和10。
既然MaxConcurrentSessions的默认值为100,那么我们的实例为什么会有200次成功的并发访问呢?原因很简单:这三个限流阀值都是针对单个处理器的,由于运行机器采用双核处理器,自然就是200。