服务实例模式
真正实现逻辑是在Service instance中发生的,client调用最终产生Service Instance的过程叫Activation(激活)。在Remoting中我们有两种Activation方式:Server Activation(Singleton和SingleCall),Client Activation。
实际上对WCF也具有相似的Activation。不过WCF不仅仅创建对应的Service Instance,而且还构建相关的Context, 我们把这些统称为Instance Context。不同的Activation方式在WCF中体现为的Instance context model。不同的Instance Context Mode体现为Proxy、Service 调用和Service Instance之间的对应关系。可以这么说,Instance Context Mode决定着不同的Session表现。在WCF中,支持以下3中不同级别的Instance Context Mode:
1>.单调服务(Per-Call Service):每次的客户端请求分配一个新的服务实例。类似于Net Remoting的SingleCall模式;
2>.会话服务(Sessionful Service):则为每次客户端连接分配一个服务实例。类似于Net Remoting的客户端激活模式;
WCF的默认实例模式是PerSession,不同的实例模式,对Session的支持也不同。当然,首先Session的支持是通过Sessionful Channel来实现的,所以如果选择了不支持Session的绑定,那其他都是没用的。比如BasicHttpBinding就不支持Session)。
各种实例模式的应用场合?
PerCall
在传统模式C/S模式的应用程序中,通常情况下存在这样的问题:客户端请求服务端之后,服务端并不是马上对处理客户端请求时需要的资源进行释放,服务端往往自作多情的认为客户端一定是个老主顾,会时不时来消费一番。可他却没想到,有的时候,客户端是个昧良心的家伙,就算服务端再怎么献殷勤,客户端也不买账。而对于一些非常珍贵的资源,比如数据库连接,文件,图像,通讯端口等。服务这种做法往往会使这些资源长期被不来消费的客户端空闲占用,当有新的请求真正要用使用他们的时候,却因为资源耗尽而无法处理。这样对服务端就得不偿失了,而PerCall就是对上面提到问题的一种解决方案。它采用类似快餐式的经营方式,当一个请求操作来到的时候,再创建服务对象,申请必要资源,而当操作完毕之后,立即销毁对象并释放资源,留给下一个请求。这就可能大大提高服务端的吞吐能力。而且WCF中默认的实例创建模式就是这种。
PerSession
正如上面对PerCall的描述所说,PerSession与传统的C/S模式应用程序非常相似,它能在服务端和客户端维护状态,当一个服务对象创建之后不会马上销毁,而是等待客户端再次来消费它,那这种的坏处也说过了,可能会浪费宝贵的服务资源,可它也是有好处的。比如它能够保持连接和维护状态,这在要求有回调的情况下特别重要,因为如果服务端连哪个家伙点的菜都忘记了怎么为客人上菜呢?还有一种情况,服务端操作不需要比较多的资源或者占用的资源也不宝贵的情况下,而却与客户端在不同的网络中,它们之间进行一次连接可费了老劲,这时也适用于此种实例模式。
Single
大家经常去理发吧?去那大的理发店,里面的理发师这家伙这个多,你随便找个就能帮你料理了,可兄弟我比较穷酸,每次都去小区理发店,里面连洗头,在理发就1位师傅,小区人可不少,僧多饭少,你说咱进去得排队吧。人家理发的时候,咱就得边上看着,得人家都整完了,嘿,咱就洗头,理发,吹风来个一条龙。Single模式就像上面提到的小区理发店,人家从早晨一开业,理发师就给你准备好了,您也甭挑蹦捡。伺候完你,他再伺候别人,为何要这么做?其实道理也很简单,如果理发师不喝水,不吃饭,不用管食宿,不用工钱,不会唧唧歪歪,那理发店老板娘肯定请1万个过来。可现实不是那样子的,是这些理发师都要吃喝拉撒睡,还要拿俸禄,就一个几十平米的小理发店,估计给老板娘卖了也不够他们的呢。
使用不同的实例模式,需要注意的有哪些?
1 对于PerCall模式,一定要记住,如果服务对象中的数据没有固化,并且不是静态变量,那它每次操作都会被重新初始化。
2 对于PerSession模式,第一要清楚有些Binding是不能用于此种模式的,具体什么可用,什么不能用,可以查阅http://www.cnblogs.com/jillzhang/archive/2008/02/03/1063406.html 。另外,PerSession模式并不是代表状态会自动维护,那些被设置了IsTerminating=True的操作完成的时候,也会释放资源和销毁对象。即使不是Ture,那如果客户端长时间不与服务端联系,达到服务端最大忍耐限度,服务端也会变心。
3 对于Single模式,既然例外就它老哥一个,就简单得多了,它能保持服务对象中的非静态全局变量。但是特别要注意的是,如果在这种模式下的话,要特别注意线程安全的问题,让10个人同时让一个理发师傅来服务。
3总结
A.首先当binding不支持session时候,所有设置都不会支持Session;单调和会话模式都是调用不同的实例,只有单例模式调用相同实例。
B.当我们设置SessionModel为NotAllowed(TCP绑定下,不可以设置SessionModel为NotAllowed,否则会出现这个错误“协定不允许会话,但是绑定“NetTcpBinding”不支持数据图,或者因配置不正确而无法支持数据图。”)时(其实等同于A)
l 如果是单调模式,不存在SessionId,每次调用不同实例
l 如果是会话模式,不存在SessionId,每次调用不同实例(实际就等于单调模式)
l 如果是单例模式,不存在SessionId,调用相同实例
C.设置SessionModel为Allowed/Required
l 如果是tcpBinding,所有Proxy的SessionId相同,下面讨论mexHttpBinding
l 单调模式:只有同一个Proxy的SessionId相同,实例不同
l 会话模式:同一个Proxy的实例SessionId相同,同一个Proxy的实例相同
l 单例模式:同一个Proxy的实例SessionId相同,所有Proxy实例相同
WCF分布式开发步步为赢(9):WCF服务实例激活类型编程与开发
我的WCF之旅(8):WCF中的Session和Instancing Management
Programming WCF Services翻译笔记(七)
并发
当一个消息到达时,服务模型会从线程池中取出一个线程来传送消息。这时,如果多个客户端调用相同的服务,多个并发请求会同时到达该服务。PerCall服务会为每个请求分配一个新的服务对象。PerSession服务,同一个服务对象从同一个客户端接收请求。对于Single实例模式,所有的客户端请求都被发送到同一个单利服务对象。在此基础上,当客户端是多线程的时候,PerSession服务就有并发访问的肯那个,而对于Single服务,该危险永远是存在的。
默认情况下,无论哪种实例化模式,只有一个请求线程可以获准访问所有服务对象。这是设置ServiceBehaviorAttribute的ConcurrencyMode属性,默认是Single。他有三个值可选:
l Single:一个一的请求线程能够在某一特定时间访问服务对象
l Reentrant(重入):一个单一的请求线程能够反问这些服务对象,但线程可以退出这项服务,并重新输入而不会死锁。
当一个服务是回调给客户的时候,重入模式是必要的,除非这个回调的是一个单向操作。因为,客户调用服务并等待响应。客户在其调用线程中受阻,直到调用返回。服务发送一个回调给客户并等待响应。这肯定会发生死锁。设置重入模式,可以避免这种问题,从服务到客户端的调用会返回给服务实例而不会引起死锁。
l Multiple:多个请求线程能够访问这些服务对象和共享资源。
[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single)]
public class CalculateService : ICalculate
限流
限流“允许开发者限制客户端连接数以及服务的负荷。限流可以避免服务的最大化,以及分配与使用重要资源的最大化。引入限流技术后,一旦超出配置的设置值,WCF就会自动地将等待处理的调用者放入到队列中,然后依次从队列中取出。在队列中等待处理调用时,如果客户端的调用超时,客户端就会获得一个TimeoutException异常。每个服务类型都可以应用限流技术,也就是说,它会影响到服务的所有实例以及服务类型的所有终结点。实现方式是为限流与服务使用的每个通道分发器建立关联。
为了增加服务的吞吐量,必须允许进行多重并发调用。不管哪种实例模式,服务器资源一般没有处理无线数量的并发请求的能力。WCF提供了一个限流行为来管理服务器负载和资源消耗。该行为具有以下属性:
MaxConcurrentCalls(一个服务实例可以处理的所有并发请求数量)、MaxConcurrentSessions(限制服务允许的活动会话的数量)、
MaxConcurrentInstances(限定某一特定时刻可以分配的服务实例数量),它们分别的默认值为16,10和Int.MaxValue。
<system.serviceModel>
<services>
<service name = "MyService"
behaviorConfiguration = "ThrottledBehavior"> ... </service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name =
"ThrottledBehavior">
<serviceThrottling
maxConcurrentCalls = "12"
maxConcurrentSessions = "34"
maxConcurrentInstances = "56" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
WCF并没有提供关于限流的特性。但实现该特性的方法非常简单,如下内容是我定义的关于限流的特性,本书并没有提供:
public class ServiceThrottlingAttribute : Attribute, IServiceBehavior
{
private ServiceThrottlingBehavior throttle;
public ServiceThrottlingAttribute(
int maxConcurrentCalls,
int maxConcurrentInstances,
int maxConcurrentSessions)
{
this.throttle = new ServiceThrottlingBehavior();
throttle.MaxConcurrentCalls = maxConcurrentCalls;
throttle.MaxConcurrentInstances = maxConcurrentInstances;
throttle.MaxConcurrentSessions = maxConcurrentSessions;
}
#region IServiceBehavior Members
void IServiceBehavior.AddBindingParameters(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ }
void IServiceBehavior.ApplyDispatchBehavior
(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
ServiceThrottlingBehavior currentThrottle =
serviceDescription.Behaviors.Find<ServiceThrottlingBehavior>();
if (currentThrottle == null)
{
serviceDescription.Behaviors.Add(this.throttle);
}
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{ }
#endregion
}
定义的ServiceThrottlingAttribute特性继承了Attribute,并实现了IServiceBehavior接口。在特性内,则使用了ServiceThrottlingBehavior类,以设置限流的相关值。如果要配置服务的限流值,就可以应用该特性,例如:
[ServiceThrottling(12, 34, 56)]
class MyService : IMyContract,IDisposable
{
public void MyMethod( )
{
ChannelDispatcher dispatcher =
OperationContext.Current.Host.ChannelDispatchers[0]
ServiceThrottle serviceThrottle = dispatcher.ServiceThrottle;
Trace.WriteLine("MaxConcurrentCalls = " + serviceThrottle.MaxConcurrentCalls);
Trace.WriteLine("MaxSessions = " + serviceThrottle.MaxConcurrentSessions);
Trace.WriteLine("MaxInstances = " + serviceThrottle.MaxConcurrentInstances);
}
}
则输出结果为:
MaxConcurrentCalls = 12
MaxSessions = 56
MaxInstances = 34