最近抽空看了下.net下的通信框架wcf,发现微软在该框架下已经实现了很多功能,用户可以通过少许的代码或者简单的配置就能实现一个soap服务。先将最近的学习画了个思维导图,后续还可以按图索骥。
一、实例上下文
WCF提供了三种不同的实例上下文模式(实例:首先我们定义的服务是一个个的类,要使用这些服务,需要先将服务对应的类实例化,也就是new)
1、Per-Call:每一次服务请求对应一个服务实例,请求到来后马上new,请求结束后马上释放资源。CLR实例的过程中,消耗的时间正常情况下是可以接受的。
2、Per-Session:同一个用户会话对应一个会话,会话不结束,实例是不会释放的,当同一时刻连接上来的用户比较多,需要关注资源可能不够用。WCF的会话最终取决于以下三个方面的因素:
1)服务契约采用Allowed或者Required的会话模式
2)服务采用PerSession的实例上下文模式
3)终结点的绑定提供对会话的支持
即使我们通过ServiceBehaviorAttribute特性将服务的实例山下文模式设置成Perssion,如果不满足其余两个条件,WCF仍然采用的是基于单调的实例上下文提供机制,那么表现出来的并发处理行为就与单调模式并无二致。
3、Single:**所有的用户请求,都是由同一个实例来处理**,实例是在应用启动的时候创建,应用关闭或者卸载的时候才释放。微观上,多个用户请求是串行处理,多个请求同时到来会先进入请求队列,对首先被执行,其它等待,如果剩下的请求在限定时间未被处理,会抛出异常TimeOutException。
二、三种并发策略(并发是基于某一个实例上下文)
1) single:一个实例上下文**在某个时刻只能用于对单一请求的处理,或者说针对某个实例上下文的多个并发的请求会以一种串行的方式进行处理。
2) Reentrant:一个实例上下文对象在某个时刻只能用于对单一请求的处理。如果服务操作在执行过程设计对客户端的回调(callback)**,该实例上下文可以用于其他服务调用请求的处理。如果回调操作执行后服务实例上下文没有用于其他请求的处理,回调后的操作能够得到处理。
3) Multiple:一个实例上下文可以同时用于处理多个服务请求。
注意:如果采用single和Reentrant模式,当WCF服务端接收到多个针对相同实例上下文的请求时,会先确定该实例上下文是否可用(是否正在处理之前的抵达服务调用请求)。如果可用,则将接收到的第一个请求分发给它,其他请求则根据抵达的先后顺序被放入一个队列中。如果之前的请求被正常处理,队列中的第一个请求被分发给实例上下文。如果一个请求在队列中等待的时间过长,超过了设置的服务调用超时时间,客户端会抛出TimeoutExceptionTimeoutException异常。
并发模式的采用是服务端单边的选择,所以并发模式以服务端服务行为的方式定义。只需要在服务类型上应用ServiceBehaviorAttribuye特性为其ConcurrencyMode属性设置相应的值即可。如果没有显式制定服务采用的并发模式,默认使用的是ConcurrencyMode.Single。
回调(callback)中的并发:WCF并发解决的是同一个实例上下文在处理并发请求时采用怎样的处理策略。实例上下文不仅仅是封装真正服务实例的容器,当服务回调客户端的时候,回调对象也是封装在实例上下文中的。我们将用于封装服务实例和回调对象的实例上下文分别称为服务实例上下文和回调实例上下文。
在双向通信的场景中,如果在进行多次服务调用的时候指定相同的回调实例上下文,就有可能出现服务并发地回调同一个回调上下文的情况。不论是多个服务请求并发调用同一个服务实例上下文,还是多个服务操作并发回调同一个回调实例上下文,WCF都采用相同的并发机制。回调采用的并发模式通过应用在回调类型上的CallbackBehaviorAttribute中的ConcurrencyMode属性来指定。
事务行为与并发:在服务类型上线应用ServiceBehaviorAttribute特性将ReleaseServiceInstanceOnTransactionComplete属性设置成TRUE,让WCF再事务结束之后将服务实例上下文释放掉。不过这样的设置,要求并发模式必须是Single。原因是:如果通过ServiceBehaviorAttribute特性将ReleaseServiceInstanceOnTransactionComplete属性设置为True,意味着当事务被提交之后当前的实例上下文会被释放掉。如果当前的并发模式不是Single,当前的实例上下文可能正在处理其他调用请求。对其进行贸然的释放自然是不允许的
三、并发中的同步(使用同一个实例时如何加锁同步)
Multiple采用并行的执行方式,而Single和Reetrant则采用串行的执行方式,串行执行即同步执行,在WCF并发框架体系中同步机制如下:
1) ConcurrencyMode.Multiple:
Multiple并发模式根本就不能再对InstanceContext加锁的问题。
2)ConcurrencyMode.Single模式下的同步实现:
WCF服务端运行时在处理服务调用消息请求之后,利用实例上下文提供者(InstanceContextProvider)创建性的或者获取现有的实例上下文,然后将请求消息分发给实例上下文对消息进一步处理。在处理操作执行之前,如果发现相应的服务采用Single并发模式,只有在获取了针对实例上下文的ThisLock属性对象的锁的情况下才会进行后续的操作。这样就保证了单一的实例上下文在Single并发模式下永远是以同步方式呗调用的。
2) ConcurrencyMode.Reentrant(重入):
当服务端进行回调时,由于加载实例上下文上的锁会被释放,意味着其他服务调用请求可以被分发给该实例上下文。当回调返回时,如果实例上下文正用于处理另一个服务调用请求,会等待实例上下文被释放。如果等待时间超过设定的超时时限,客户端会抛出TimeoutException异常。
注意::实例上下文决定的是实例数,并发模式决定的是一个实例能并发处理请求的情况。
四、WCF中同步上下文与线程亲和性
在默认情况下,如果服务寄宿(IIS/WAS寄宿方式除外)线程存在同步上下文,会将其保存在服务端分发运行时中。在执行服务操作的时候,WCF会判断分发运行时的同步上下文是否存在,如果不存在,则在各个线程中执行服务操作,否则服务操作会被封送到该同步上下文中执行。如果将某个服务寄存于一个控制台应用,由于控制台应用程序的当前同步上下文为空,所有的请求操作会在各自的线程并行地执行。所以在流浪允许的范围内,并发的请求能够得到及时的处理。如果将windowForm应用作为某个服务的宿主,由于UI线程总是具有一个类型为windowsFormsSynchronizationContext的同步上下文,服务操作的执行永远以同步的方式执行。
我们将服务操作与服务寄程序线程自动绑定的现象称为服务的线程亲和性(Thread
Affinity)---特定的服务请求会一直由固定的线程处理。
线程亲和性使我们采用windows forms应用作为服务宿主的时候,服务操作可以直接操作窗体上的控件。但是如果希望服务操作能够并发地执行,就不得不打破这种线程亲和性。可以通过服务类型上应用ServiceBehaviorAttribute特性并将UseSynchronizationContext属性设置成False来实现。如果在服务类型上应用了ServiceBehaviorAttribute特性并将UseSynchronizationContext属性设置成False,针对窗体控件的操作就需要调用control类型的Invoke或者BeginInvoke方法,或者按照我们监控程序的方式借助于SynchronizationContext对象(调用send或者post方法)。
五、同步上下文影响回调操作的执行
对于非IIS/WAS寄宿方式,如果在进行服务寄宿的时候当前线程存在同步上下文,服务操作最终会在该同步上下文中执行。相似的情况同样发生在核对奥操作的执行上。在回调场景中,客户端进行服务调用的时候,如果当前线程存在同步上下文,那么当服务端进行回调的时候,回调操作会自动被封送到该同步上下文中执行。也就是说回调操作客户端程序也存在一种线程关联性。客户端可以通过特性标注的方式解除回调操作与客户端程序之间的线程关联性,将CallbackBehaviorAttribute特性中userSynchronizationContext属性设置为false.
六、服务代理
WCF有一个隐晦的机制:如果直接通过创建出来的服务代理对象(并没有显式开启服务代理)进行服务调用,WCF客户端框架会通过相应的机制确保服务代理的开启,可以将这种机制称为代理自动开启。在内部WCF实际上是将本次调用放入一个队列之中,等待上一个放入队列的调用结束。也就是说,针对一个没有被显式开启的服务代理的并发调用实际上是以同步或者串行的方式被调用。但是如果在进行服务调用之前通过显式方式开启服务代理,基于该代理的服务调用就能得到及时处理。
七、流量限制
Wcf是一个机遇多线程的消息监听、接收和处理框架体系,能够同时应付多个来自相同或者不同客户端的服务调用请求,并提供晚上的同步机制确保状态的一致性。我们期望wcf服务端能够够处理尽可能多的并发请求,但是资源的有限性决定了并发量有一个最大值。如果wcf不控制进入消息处理系统的并发量,试图处理所有抵达的并发请求,那么一旦超过这个临界值,整个服务端将会由于资源耗尽而崩溃。Wcf对限流的控制是通过一个服务行为的方式实现。通过ServiceThrottlingBehavior定义了MaxConcurrentCalls、MaxConcurrentInstances和MaxConcurrentSessions可读写属性,他们分别代表流量控制的三个阈值,这三个属性所代表的数值是针对某个ServiceHost而言:
三个阀门的判断顺序如下
1) MaxConcurrentSessions,当前serviceHost允许的最大并发会话数,默认100
2) MaxConcurrentCalls,当前servicehost能够处理的最大并发消息数量,默认是16
3) ServiceThrottlingBehavior,当前servicehost允许存在的服务实例上下文的最大数量,默认是116
对于WCF4.0来说,这些指标的值是针对单个处理器而言的。对于双核处理器,真正的并发量是所设置数值的两倍。
八、服务不能直接调用数据库,但是在一个设计良好的架构中,服务依赖于其他层与数据库进行通信。
九、如果被认证方以数字证书作为用户凭证,认证方一般采用信任链模式对其实施认证。在该模式下,认证方从数字证书的直接办法机构向上追溯,如果任何一个办法机构是受信任的,那么认证成功。不过有时还是会采用其他的认证模式,比如严格比较证书主题信息甚至是序列号。
十、nettcpbinding的security属性返回的是system.servicemodel.nettcpsecurity对象。表示安全模式的nettcpsecurity的mode属性返回的是我们提到过的securitymode枚举,securitymode支持安全模式none、transport、message和mixed(混合模式,认证时先采用message模式,后面的数据加密采用transport模式)。Transport属性认证模式有三种None、windows和certificate,默认情况下nettcpbinding采用windows凭证认证。
Message模式下,也是具有None、windows和certificate三种凭证认证方式,默认情况下采用的也是windows凭证认证
十一、服务认证:
Wcf中的认证属于双向认证,即包括服务端对客户端的认证,也包括客户端对服务端的认证。客户端认证和服务端认证从本质上没有什么区别,无非都是被认证一方提供相应的用户凭证供对应方对自己的身份进行验证。
其它补充:
1)只有服务代理主动执行close的时候,才会关闭同服务端的连接
2)single上下文模式或者Persioon上下文模式(多线程客户端同时访问)会涉及线程同步问题:
并发模式决定是否允许多线程同时访问一个实例对象
single 表示实例对象一次只能有一个线程访问,此模式下不会产生同步问题,其他的请求被插入等待队列,
只有当前线程释放了此实例对象后,其他请求才允许访问它
multiple允许多个线程同时访问一个实例对象,如果有必要必须通过手工方法Monitor和Mutex等传统的.net
工具来控制线程同步问题。
3)wcf性能计数器
https://docs.microsoft.com/zh-cn/dotnet/framework/wcf/diagnostics/performance-counters/
https://www.developer.com/net/net/article.php/11087_3356561_2/Reading-and-Publishing-Performance-Counters-in-NET.htm
4)最佳做法
在Percall架构中使用Datacontractserializer和concurrencyMode.Single模式
5)负载均衡
Percall实例上下文模式:如果没有使用面向会话的协议可以使用负载均衡
Perssion、single或者面向连接的协议,用标准的WCF技术实现负载均衡不可能,特别是perssion模式下,wcf无法与一个
核心的状态服务器或数据库服务器交换会话变量,这与传统的web 中的session不一样。