WCF Throttling 限流的三道闸口
一、WCF Throttling 流量限制简介
我们期望WCF服务端能够处理尽可能多的并发请求,但是资源是有限的,服务不可能同时处理无限多的并发请求,如果WCF不控制进入消息处理系统的并发量,一旦超过临界值,整个服务端将会由于资源耗尽而崩溃,所以WCF提供了一个限流Throttling特性,让我们可以根据不同性能的服务器来配置最佳的并发流量限制,这三个值相当于WCF通道的三个闸口,每个值代表各自闸口的计数器,当各闸口监控的计数达到你所设置的阈值,后续的请求将会进入到服务的等待队列,所以,如果此值设置过小了,则大量并发请求时不能被及时处理而进入到等待队列中,很容易出现超时的情况,如果值设置过大,则会出现WCF处理不过来的情况,容易导致服务崩溃,所以,对于高低配置处理能力不同的服务器,需要分别设置阈值。
二、Throttling三个流量阈值简介
通过反编译和MSDN帮助得知,三个属性解释和默认值如下:
MaxConcurrentCalls |
该值指定整个 ServiceHost 中正在处理的最多消息数 |
16 * CPU核心数 |
MaxConcurrentSessions |
指定 ServiceHost 对象可一次接受的最多会话数的值 |
100 * CPU核心数 |
MaxConcurrentInstances |
该值指定服务中可以一次执行的最多 InstanceContext 对象数 |
MaxConcurrentCalls+MaxConcurrentSession |
从System.ServiceModel.dll 反编译的代码来看,默认值的逻辑确实如此。
但,你可能会想,一个ServiceHost默认正在处理的消息数才16,未免太少了吧,别担心,反编译代码再细看可知,MaxConcurrentCalls和MaxConcurrentSessions的默认值与CPU核数是相关的,假如你的系统配置是4核电脑,则MaxConcurrentCalls默认值等于16*4,MaxConcurrentSessions默认值等于 100*4,maxConcurrentInstances默认值等于64+400,假如你对此默认值还觉得不适当,需要显式设置的话,建议以默认值为准绳进行增减设置,MSDN Library 的建议是设置比16 * cpu核数要大的数值,总的来说,阈值设置多大,还需根据生产环境情况和经验来把握,值得注意的是,显式配置的时候,值是需要是经过与cpu核心数乘法计算后的值,因为WCF不会再去乘以你所显式设定的值的。
一个完整的ServiceThrottling配置如下,如采取默认,则将<ServiceThrottling>节点去掉,你也可以单独只设定某个属性,其他属性则自动采用默认值,除了以配置的形式外,还可以通过代码来进行设定,这里不做叙述。
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceThrottling maxConcurrentCalls="16" maxConcurrentSessions="100" maxConcurrentInstances="116"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
三、maxConcurrentCalls:使用windows性能监视器验证配置效果
我在研究ServiceThrottling特性的时候搜了很多资料,发现很少,几乎是没有一篇是说三个流量阈值该设置多少才合适、如何去验证配置前后的效果等,这让我当时有一种感叹:世界上最难的事情并不是找出正确的答案,而是让你找出证明你答案是正确的证据。
后来发现使用windows的性能监视器可以监控到WCFService的执行情况,我使用的是ServiceModelService 4.0.0.0 的Calls OutStanding计数器,能监控正在进行的调用的次数,其他关于WCF的计数器请参阅文末的参考资料;Windows性能监视器可以在管理工具->性能监视器 打开,除了实时监控外,还可以生成日志文件,实乃监控运维的一大利器,关于性能监视器的详细用法请自行搜索资料。
我的测试服务接口所做的操作是,Thread.Sleep 3秒,然后客户端大量并发请求,通过修改MaxConCurrentCalls的值观察实验效果:
1、 当将MaxConcurrentCalls设置为1的时候,服务将以串行化执行,监控结果显示正在进行的调用次数一直都为1;
2、 当将MaxConcurrentCalls设置为30的时候,WCF服务的处理能力明显提到,当调用次数在提升阶段时,证明WCF的处理能力在预设的阈值之内,当达到峰顶阈值30后,后续来的请求发现此时的WCF同时处理请求的能力已经达到预设的阈值了,就会进入到等待队列中去,等待调度,当等待队列中的请求慢慢被处理完后,此时计数器也会进行减法处理,以便让后续来的请求知道,你这个服务到底有没有能力马上“接待”我,从下图可以看出,如果MaxConCurrentCalls设置过小的话,就会很容易限制了WCF的处理能力,出现瓶颈。
四、maxConcurrentSessions:借用蒋金楠博客中的例子来说说
maxConcurrentSessions的作用是指定 ServiceHost 对象可一次接受的最多会话数的值,默认值为100 * CPU核心数,所以,我们的测试示例使用的接口Binding必须是支持Session的,不能使用basicHttpBinding,因为他不支持session会话,如果Binding不支持会话,则此属性的设置也就无效了,这里我使用的Binding是wsHttpBinding。
using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
{
bool stop = false;
for (int i = ; i < && !stop; i++)
{
ICalculator calcultor = channelFactory.CreateChannel();
try
{
calcultor.Add(, );
Console.WriteLine("第{0}个服务代理调用成功!", i + );
}
catch (Exception ex)
{
Console.WriteLine("出现异常:{0}", ex.Message);
stop = true;
}
}
}
我们通过基于客户端终结点配置名称创建的ChannelFactory<TChannel>对象创建了1000个服务代理进行用其进行1000次服务调用。当上面这个实例运行的时候,客户端控制台将会出现如下的输出结果,实例程序清晰地反映了这样的事实:虽然我们通过不同的服务代理对象进行了1000次服务调用,但是只有前面400是成功的,第401个请求将一直等待被处理,直到达到超时时间为止,在这里我没有配置maxConcurrentSessions值,所以限流阈值采用的是默认值100 乘以我的电脑CPU核心数4,也就是400。
在这里对蒋金楠表示感谢,不仅仅是在查询WCF相关资料的时候经常会搜到你的文章,对我有很大的帮助,而且上次你在博客园发起的整点抢评论赠书活动中送了我一本《ASP.NET Web API 2 框架揭秘》,据说很快会做一个《ASP.NET MVC5 框架揭秘》的抢评活动!
五、maxConcurrentInstances
MSDN:MaxConcurrentInstances 属性指定服务中 InstanceContext 对象的最大数目。 牢记 MaxConcurrentInstances 属性和InstanceContextMode 属性之间的关系很重要。 如果 InstanceContextMode 为 PerSession,则结果值将为总会话数。 如果InstanceContextMode 为 PerCall,则结果值将为并发调用的数量。 如果消息到达时,InstanceContext 对象的数目已经达到最大数量,则将保存该消息,直至有 InstanceContext 对象关闭。
InstanceContextMode是特性ServiceBehavior中的一个枚举属性,ServiceBehavior特性指定服务协定实现的内部执行行为,一般这样使用。
既然MaxConCurrentInstances与InstanceContextMode关系密切,先来看看这个枚举,很明显PerSession 与PerCall 的区别是,PerSession 为每个会话创建一个新的System.ServiceModel.InstanceContext对象,而PerCall则会在每次调用前创建,调用后回收。
//指定可用来处理包含在传入消息中的调用的服务实例数。
public enum InstanceContextMode
{
//为每个会话创建一个新的 System.ServiceModel.InstanceContext 对象。
PerSession = ,
//新的 System.ServiceModel.InstanceContext 对象在每次调用前创建,在调用后回收。如果信道未创建会话,则该值的行为就如同
PerCall = ,
//只有一个 System.ServiceModel.InstanceContext 对象用于所有传入呼叫,并且在调用后不回收。如果服务对象不存在,则创建一个。
Single = ,
}
六、总结
以上,我们得知WCF的限流特性以及三个限流闸口各自限制的对象,还可以通过Windows性能监视器来监控服务的情况,对于WCF Throttling,我个人建议是让其采取默认的配置,因为其默认值中考虑到了CPU核心数,考虑到了不同配置服务器的情况,而且还可以不必在每台服务器上修改配置,但同时MSDN也提到,WCF Throttling的设置也是需要依靠经验来,所以,如果采用默认配置的情况下出现瓶颈现象和处理能力不足的情况,不妨根据每台服务器的情况,显式增加阈值。
七、参考资料:
乘以CPU核心数的好处就是部署在不同配置的服务器上,都无需再修改配置。
We also bumped up the value for MaxConcurrentSessions from 10 to 100. Based on customer’s feedback, this change fits the need for most applications.
Please note, these changes are for the default settings only. If you explicitly set these settings in either configuration or in code, the system would use the settings that you provided. No “ProcessCount” multiplier would be applied.
* CPU核心数 的改变只适用于默认配置,如果你修改配置的话,就不适用于核心数相乘。需要你自己乘以核心数然后配置上去 。
4、WCF 4.0一个鲜为人知的改变[兼书名征集] http://www.cnblogs.com/artech/archive/2011/12/31/2308627.html
5、控制并发访问的三道屏障: WCF限流(Throttling)体系探秘[下篇] http://www.cnblogs.com/artech/archive/2010/04/14/1712088.html
6、Optimizing WCF Web Service Performance http://msdn.microsoft.com/library/ee377061.aspx
7、WCF中的Binding模型之六(完结篇):从绑定元素认识系统预定义绑定 http://www.cnblogs.com/artech/archive/2008/12/14/1354691.html
8、concurrency-and-throttling-configurations-wcf-services http://devproconnections.com/net-framework/concurrency-and-throttling-configurations-wcf-services
9、WCF中并发(Concurrency)与限流(Throttling)体系深入解析系列[共7篇] http://www.cnblogs.com/artech/archive/2010/04/22/1718188.html