WCF并发(Concurrency)的本质:同一个服务实例上下文(InstanceContext)同时处理多个服务调用请求

引言

服务(Service)的本质就是提供服务消费者期望的某种功能,服务的价值体现在两个方面:服务本身的质量和寄宿服务的平台应付消费者的数量,并发(Concurrency)的关注的是第二个要素。WCF服务寄宿于资源有限的环境中,要实现服务效用的最大化,需要考虑如何利用现有的资源实现最大的吞吐量(Throughput)。提高吞吐量就某个寄宿的服务实例(Service Instance)来说,一个重要的途径就是让它能够同时处理来自各个客户端(服务代理)的并发访问。WCF实现了一套完整的并发控制体系,为你提供了不同的并发模式。

我经常说软件架构是一门权衡的艺术,需要综合考虑各种相互矛盾的因素,找到一种最优的组合方式。提高单个服务实例允许的并发访问量能够提高整体吞吐量,这样的理论依赖于一种假设,那就是服务端所能使用的资源是无限。我们知道,这种假设无论在什么情况下都不会成立。如果我们并发量超出了服务端所能承受的临界点,整个服务端将会崩溃。所以,WCF一方面需要允许让单个服务实例并发处理接收到的多个请求,同时也需要设置一道闸门控制并发的数量。WCF的流量限制(Throttling)体系为你创建了这道闸门。

从本篇文章开始,我将发布一系列的文章对WCF并发架构体系进行深入剖析 ,先来看看并发的基本介绍。

一、同一个服务实例上下文同时处理多个服务调用请求

并发的含义就是多个并行的操作同时作用于一个相同的资源或者对象,或者说同一个资源或者对象同时应付多个并行的请求。对于WCF的并发来说,这里将的“资源或者对象”指的就是承载服务操作最终执行的服务实例(Service Instance)。而WCF将服务实例封装在一个称为实例上下文(InstanceContext)对象中,所以WCF中的并发指的是同一个服务实例上下文同时处理多个服务调用请求。

WCF服务端框架一个主要的任务是将接收到的服务调用请求分发给激活的服务实例,调用相应的服务操作并返回执行结果。也就是说,服务操作的执行最终还是会落实到某个具体的服务实例上。《WCF技术剖析(卷1)》的第9章对WCF的实例化机制进行了深入的剖析,从中我们知道在WCF服务端框架体系中,激活的服务实例并不是单独存在的,而是被封装在一个被称为实例上下文(InstanceContext)对象中。WCF提供了三种不同的实例上下模式(Per-Call、Per-Session和Single)实现了不同的服务实例上下文提供机制。

所以,WCF并发框架体系解决的是如何有效地处理被分发到同一个服务实例上下文的多个服务调用请求,这些并行的调用请求可能来自不同的客户端(服务代理),也可能相同的客户端。WCF并发的本质上可以通过图1体现。

clip_image002

图1 通过一个InstanceContext对多个并发请求的处理

由于WCF的并发处理属于服务本身自身的行为,所以我们通过服务行为(Service Behavior)的形式对采取的并发策略进行控制,而不同的并发策略定义在相应的并发模式(Concurrency Mode)下面。

二、通过ServiceBehaviorAttribute特性定义并发模式

WCF为三种典型的并发处理策略定义了三种典型的并发模式,即Single、Reentrant和Multiple。这三种并发模式通过ConcurrencyMode的三个同名的枚举项表示,ConcurrencyMode定义如下:

   1: public enum ConcurrencyMode
   2: {
   3:     Single,
   4:     Reentrant,
   5:     Multiple
   6: }

通过ConcurrencyMode枚举项表示的三种不同的并发模式体现了WCF处理并发请求的三种不同能策略:

  • Single一个封装了服务实例的InstanceContext对象在某个时刻只能用于对某一个单一请求的处理,或者说针对某个InstanceContext对象的多个并发的请求会以一种串行的方式进行处理。具体来讲,当WCF服务端框架接收到多个针对相同InstanceContext的请求时,会先确定该InstanceContext是否可用(是否正在处理之前的服务调用请求),如何可用,则将接收到的第一个请求分发给它,其它请求则被放入根据抵达的先后顺序被放入到一个队列中。如果之前的请求被正常处理,队列中的第一个请求被分发给InsanceContext。如果一个请求在队列中等待的时间过长,超过了设置好的服务调用的超时实现,客户端会跑出TimeoutException异常;
  • Reentrant该模式和Single一样,InstanceContext对象在某个时刻只能用于对某一个单一请求的处理。不过有一点不同的是,如果服务操作在执行过程中涉及对外调用(Call Out),该InstanceContext可以用于其它服务调用请求的处理;
  • Multiple在该模式下,一个InstanceContext可以同时用于处理多个服务请求,所以Multiple并发模式下针对同一个InstanceContext的多个并发请求能够得到及时的处理。不过,由于是并行的处理方式,服务操作执行过程中状态的管理以及多线程的安全问题需要服务开发者自行处理。

并发模式的采用是服务单边的选择,是服务端个人的行为,所以并发模式以服务行为的方式定义,我们只需要在服务类型上应用ServiceBehaviorAttribute特性,为ConcurrencyMode属性设置相应的值即可,ServiceBehaviorAttribute定义如下:

   1: [AttributeUsage(AttributeTargets.Class)]
   2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
   3: {
   4:     //其它成员
   5:     public ConcurrencyMode ConcurrencyMode { get; set; }
   6: }

如果显示指定服务采用的并发模式,默认使用的是ConcurrencyMode.Single,所以下面两种服务定义方式是等效的。

   1: public class CalculatorService : ICalculator
   2: {
   3:     //省略成员
   4: }
   5:  
   6: [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single)]
   7: public class CalculatorService : ICalculator
   8: {
   9:     //省略成员
  10: }

三、回调(Callback)中的并发

WCF并发解决的是同一个InstanceContext对象在处理并发请求是采用怎样的处理策略。我们知道InstanceContext不仅仅是封装真正服务实例的容器,当我们通过双向通信的机制从服务端回调客户端操作时,真正执行回调操作的回调对象也是封装在InstanceContext中。

在双向通信的场景中,如果多个服务端或者同一个客户端的多个并发的服务调用操作所指定的回调实例上下文(即封装回调操作的InstanceContext对象),就可能出现针对同一个InstanceContext的并发回调的现象。WCF采用与正常服务调用相同的机制来处理并发回调,实际上WCF采用几乎一样的机制来实现正常的服务调用和回调。

与通过将ServiceBehaviorAttribute特性应用到服务类型并指定采用的并发模式相类似,回调采用的并发模式通过应用在回调类型上的CallbackBehaviorAttribute特性来指定。CallbackBehaviorAttribute中同样定义了ConcurrencyMode属性:

   1: [AttributeUsage(AttributeTargets.Class)]
   2: public sealed class CallbackBehaviorAttribute : Attribute, IServiceBehavior
   3: {
   4:     //其它成员
   5:     public ConcurrencyMode ConcurrencyMode { get; set; }
   6: }

下面的代用中,我们通过在回调类型CalculatorCallbackService上应用CallbackBehaviorAttribute特性,将回调并发模式设置成ConcurrencyMode.Multiple。

   1: [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
   2: public class CalculatorCallbackService:ICalculatorCallback
   3: {
   4:     //省略成员
   5: }

四、 事务行为与并发

相信你还会记得在上面一章介绍事务编程(《上篇》、《中篇》、《下篇》)的时候,可以在服务类型上面应用ServiceBehaviorAttribute特性将ReleaseServiceInstanceOnTransactionComplete属性设成True,这样可以让WCF在事务结束之后将封装了服务实例的InstanceContext对象释放掉。不过这样的设置之后再并发模式为ConcurrencyMode.Single的前提下方才有效,否则在进行服务寄宿的时候将会抛出异常。

比如说,我们定了如下一个BankingService服务类型,通过ServiceBehaviorAttribute特性指定ReleaseServiceInstanceOnTransactionComplete为True,并采用ConcurrencyMode.Multiple并发模式。

   1: [ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = true,ConcurrencyMode = ConcurrencyMode.Multiple)]
   2: public class BankingService : IBankingService
   3: {
   4:     [OperationBehavior(TransactionScopeRequired = true)]
   5:     public void Transfer(string accountFrom, string accountTo, decimal amount)
   6:     {
   7:        //省略实现
   8:     }
   9: }

当我们试图寄宿该BankingService服务的时候,如图2所示的InvalidOperationException异常会被抛出,并提示对于已经将ReleaseServiceInstanceOnTransactionComplete设置成True的服务来说,必须将并发模式设成ConcurrencyMode.Multiple。

clip_image004

图2 在Multiple+ReleaseServiceInstanceOnTransactionComplete导致的异常 

WCF提供的三种不同的并发模式,使开发者可以根据具体的情况选择不同的并发处理的策略。对于这三种并发模式,Multiple采用的并行的执行方式,而Single和Reentrant则是采用串行的执行方式。串行执行即同步执行,在WCF并发框架体系中,这样的同步机制是如何实现的呢?请关注下篇文章

作者:Artech
出处:http://artech.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2010-03-19 20:33 Artech 阅读(4496) 评论(34) 编辑 收藏

 回复 引用 查看   
#1楼 2010-03-19 20:39 麒麟      
周五 再抢个沙发
 回复 引用 查看   
#2楼 2010-03-19 20:42 麒麟      
楼主 我一直想把WCF学好 但是你的文章太多太多 不知道如何下手呀(我看过其中一些)
 回复 引用 查看   
#3楼[楼主] 2010-03-19 20:44 Artech      
引用麒麟:楼主 我一直想把WCF学好 但是你的文章太多太多 不知道如何下手呀(我看过其中一些)

可以按照《WCF技术剖析(卷1)》的顺序,呵呵!

 回复 引用 查看   
#4楼 2010-03-19 20:50 yyww      
好文。
 回复 引用 查看   
#5楼 2010-03-19 20:53 麒麟      
呵呵 我很穷呀 但还是打算去买第一卷。还有我很想知道您学习WCF的历程。
 回复 引用 查看   
#6楼[楼主] 2010-03-19 20:54 Artech      
引用yyww:好文。

thanks!

 回复 引用 查看   
#7楼[楼主] 2010-03-19 21:02 Artech      
引用麒麟:呵呵 我很穷呀 但还是打算去买第一卷。还有我很想知道您学习WCF的历程。

我学WCF最初的动机就是工作需要,加上之前对Remoting用的比较多,所以多WCF那一套理解起来觉得挺容易。
觉得WCF是比较实用的东西,所以就深入学习了一段时间,加工作的时间,才有了一些沉淀,才有了写书的冲动。
实际上,对WCF更加深层次的了解还是在写作《WCF技术剖析》的过程中。我的目的是写作一本与现有WCF书籍均不一样的书,重压之下,迫使我花了很大的力气研究WCF深层次的东西!

 回复 引用 查看   
#8楼 2010-03-19 21:28 Jeffrey Zhao      
请问如果我要关注WCF性能方面的内容我该参考哪些资料呢?
 回复 引用 查看   
#9楼[楼主] 2010-03-19 21:57 Artech      
引用Jeffrey Zhao:请问如果我要关注WCF性能方面的内容我该参考哪些资料呢?

我觉得可以考虑下面一些影响性能的因素:
1、绑定的选择
选择适合具体场景的绑定,能够使用TCP协议就不使用HTTP,能够使用二进制编码就不选择文本编码,如果不需要安全机制,就将安全模式设置成None,如果需要消息安全,不需要Negotiation,就Negotiation关闭,如果不需Reliable Messaging,就将不要配置ReliableSessions,...
2、事务处理
如果需要能够使用本地事务就能处理的,就不要采用分布式事务,如果需要采用分布式事务,能够使用OleTx,就不要采用WS-AT,...
3、实例化
对于Per-Session实例上下文模式来说,如果采用会话信道进行通信,务必及时将信道关闭
4、并发
如果采用相同的服务代理进行并发调用,务必先将代理显式开启;默认采用的Single模式的串行处理,如果需要并发调用,务必将并发模式设置为Multiple,这对于Singleton实例模式尤为重要。服务操作的默认执行于寄宿服务的同步上下文中,所以如果采用Windows Form应用进行服务寄宿,需要通过ServiceBehaviorAttribute将UseSynchronizationContext设置为False,....
5、流量控制
根据部署资源对并发请求数量进行最优配置,达到对硬件资源和软件资源的高效使用

暂时就想到这些

 回复 引用 查看   
#10楼 2010-03-19 22:24 Jeffrey Zhao      
@Artech
有啥资料可以参考不?

 回复 引用 查看   
#11楼[楼主] 2010-03-19 22:51 Artech      
引用Jeffrey Zhao:
@Artech
有啥资料可以参考不?

暂时没有值得推荐的资料:)

 回复 引用 查看   
#12楼 2010-03-19 23:22 Flyear      
我如果采用异步通信,同时发送多个请求,我该如何来关闭信道呢?
 回复 引用 查看   
#13楼 2010-03-19 23:48 SuperLiangG      
楼主 辛苦啦~~ 谢谢写出这么好的文章
 回复 引用 查看   
#14楼[楼主] 2010-03-20 09:01 Artech      
引用Flyear:我如果采用异步通信,同时发送多个请求,我该如何来关闭信道呢?

如果你指的是采用One-way的消息交换模式(MEP),当你调用某个方法后,只要消息进入通信层,就会马上返回,此时客户端信道已经不在需要,你可以调用Close方法将其关闭!

 回复 引用 查看   
#15楼[楼主] 2010-03-20 09:01 Artech      
引用SuperLiangG:楼主 辛苦啦~~ 谢谢写出这么好的文章

谢谢!

 回复 引用 查看   
#16楼 2010-03-20 09:44 geff zhang      
引用Jeffrey Zhao:请问如果我要关注WCF性能方面的内容我该参考哪些资料呢?

可以看这篇文章 http://msdn.microsoft.com/en-us/library/bb310550.aspx

 回复 引用 查看   
#17楼[楼主] 2010-03-20 10:03 Artech      
引用geff zhang:
引用Jeffrey Zhao:请问如果我要关注WCF性能方面的内容我该参考哪些资料呢?

可以看这篇文章 http://msdn.microsoft.com/en-us/library/bb310550.aspx

当初微软进行WCF推广的时候,老拿这篇文章的数据说事儿,呵呵!

 回复 引用 查看   
#18楼 2010-03-20 11:17 Flyear      
@Artech
我客户端是Silverlight,应该不是One-Way模式, 客户端的请求就是以已不模式请求的, 可否说一下这种情况下异步请求的关闭.

文章看起来,真清爽啊
 回复 引用 查看   
#20楼[楼主] 2010-03-21 16:42 Artech      
引用Flyear:
@Artech
我客户端是Silverlight,应该不是One-Way模式, 客户端的请求就是以已不模式请求的, 可否说一下这种情况下异步请求的关闭.

如果是这样,服务调用和WCF没有什么关系,对于变成来讲,就是一个普通的方法调用而已。采用任何一种异步编程方式都可以实现,比如利用IAsyncResult,WaitHandle, Callback可以很容易地实现服务调用后对服务代理的关闭!

 回复 引用 查看   
#21楼[楼主] 2010-03-21 16:45 Artech      
引用吉日嘎拉>不仅权限设计:文章看起来,真清爽啊

这得力于采用的模板和样式吧!
对了,很荣幸收到你的邀请,不过限于目前这样的工作状况,很难抽身做其他的事情,还望见谅。
本来想通过消息系统回复的,可能是你的名称具有一个特殊的符号〉,发送消息的时候总是提示收信人不存在!

 回复 引用 查看   
#22楼 2010-03-21 22:46 森林总是晴天      
很喜欢WCF,楼主的文章很适合增加积累 谢谢分享
 回复 引用 查看   
#23楼[楼主] 2010-03-21 23:09 Artech      
引用森林总是晴天:很喜欢WCF,楼主的文章很适合增加积累 谢谢分享

thanks!

@Artech
以后应该机会也很多,我是很佩服您对wcf的深入研究,等方便时,我去买你的书看看。

 回复 引用 查看   
#25楼 2010-04-23 08:09 海洋之 心      
我靠,楼主也太猛了,我都么入门,你都写书了,诶
 回复 引用 查看   
#26楼[楼主] 2010-04-23 08:17 Artech      
引用海洋之 心:我靠,楼主也太猛了,我都么入门,你都写书了,诶

只能说我入门稍早的一点而已!

 回复 引用 查看   
#27楼 2010-04-27 23:07 Everlonely      
引用当我们试图寄宿该BankingService服务的时候,如图2所示的 InvalidOperationException异常会被抛出,并提示对于已经将 ReleaseServiceInstanceOnTransactionComplete设置成True的服务来说,必须将并发模式设成 ConcurrencyMode.Multiple。


这里应该有个笔误吧,应该改成“必须将并发模式设成 ConcurrencyMode.Single”

 回复 引用 查看   
#28楼[楼主] 2010-04-28 08:18 Artech      
引用Everlonely:
引用当我们试图寄宿该BankingService服务的时候,如图2所示的 InvalidOperationException异常会被抛出,并提示对于已经将 ReleaseServiceInstanceOnTransactionComplete设置成True的服务来说,必须将并发模式设成 ConcurrencyMode.Multiple。


这里应该有个笔误吧,应该改成“必须将并发模式设成 ConcurrencyMode.Single”

确实是个笔误,谢谢指正!

 回复 引用 查看   
#29楼 2010-07-27 09:48 wcfPK      
博主,你好,请教个问题。
IIS托管WCF是不是不支持回调、单例服务、流处理、事务传播等特性呀。
买了你的《WCF技术剖析(卷1)》看了,感觉收获很大,我现在做个项目,想对每种wecf服务宿主的优点和缺点有个详细了解,博主在这方面能不能给点意见。我第一次用wcf做项目,学习中^_^

 回复 引用 查看   
#30楼[楼主] 2010-07-27 10:56 Artech      
引用wcfPK:
博主,你好,请教个问题。
IIS托管WCF是不是不支持回调、单例服务、流处理、事务传播等特性呀。
买了你的《WCF技术剖析(卷1)》看了,感觉收获很大,我现在做个项目,想对每种wecf服务宿主的优点和缺点有个详细了解,博主在这方面能不能给点意见。我第一次用wcf做项目,学习中^_^

回调、单例服务、流处理、事务传播的支持与Host方式无关。

 回复 引用 查看   
#31楼 2010-10-14 10:20 garry      
此文不错,值得推荐!
 回复 引用 查看   
#32楼 2011-02-22 16:19 craystall      
我使用了ConcurrencyMode.Multiple, 但是由于客户端程序一次请求的并发数比较多,部分请求失败,请问该如何处理?
 回复 引用 查看   
#33楼 2011-04-10 17:02 builderman      
我目前的情况是这样子的:

局域网内有四台服务器,
A服务器上面部署的是一个对外的网站
B服务器上面部署的是一个WCF服务
C服务器上面部署的也是一个WCF服务
D服务器上是一个sql server 数据库

通过A调用B, B调用C, C最终把数据保存到D上面的数据库去

A是一个对外的网站, 不同时段的访问量不一样, 在收集到数据后,会放入队列, 然后开多个线程把数据传给B
B只是做一个转发,把数据给C
C最终把数据保存到数据库

我想知道的是:
1.B跟C上面部署的两个WCF服务要如何设置,才能够保证高效的处理这些数据;
2.WCF在绑定用的是TcpNetBinding, 不知道MaxConntions要设置成多少才合适;
3.另外,在WCF服务端应该如何合理的设置限流(MaxConcurrentCalls,MaxConcurrentInstances,MaxConcurrentSessions这三个参数该如何配置);
4.实例管理与并发管理如何设置才合适,目前用的都是InstanceContextMode.PreCall, ConCurrencyMode.Multiple

THKS

 回复 引用 查看   
#34楼 2011-05-27 16:13 yzixuan      
现在也同样遇到33楼的问题,很想知道楼主是如何设计的,期待楼主的回复。3q

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1690118 vfKR0TlLpVk=