WCF学习笔记之实例管理
WCF实例管理是一系列技术的总称,他的任务是将客户端的请求绑定到服务实例上,并根据客户端请求的类型来确定服务实例的管理方式。由于应用程序的吞吐量、事务、队列调用等方面的巨大差异,所以我们需要对实例进行管理。
一 WCF支持三种实例激活模式
1.1 单调服务
[ServiceContract]
public interface IMember
{ ... }
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class MemberService : IMember
{ ... }
在经典的客户端/服务器的编程模型中,如果该应用成为多个客户端提供服务,而每个客户端都会拥有自己的专门的服务器对象,这些对象在客户端启动时创建关闭时被释放。然后客户端可能在很长一段时间并没有使用这个对象,而这些对象又可能掌控着昂贵的资源。这样一来应用程序的可伸缩性将面临严峻的考验。
那么一个良好的激活方式是在调用过程中被分配对象,在存在并发访问时创建多个对象。在调用终止时释放资源。
当服务被配置为单调激活方式时,服务实例(CLR对象)只存在于客户端的调用过程中(而非整个生命周期内)。每次客户端发出的请求(WCF契约方法的调用)会获得一个新建的专门的服务实例。流程如下:
客户端调用代理,代理将调用转发给服务
WCF创建一个服务实例,然后调用服务实例的方法
当方法调用返回时,实例被消耗。
在调用期间,客户端只是获得一个代理的引用而不是服务对象,这就是说服务器端可以任意释放资源。只有在客户端有真正需要时才获得资源。这样一来只是在重复创建和销毁服务实例,这比起重复创建实例与连接来说消耗资源会小很多。
由于WCF需要主动管理实例状态,所以需要保存实例状态,这必然对性能带来影响,却换来了系统的可伸缩性。这是一个鱼和熊掌的问题。通常,如果该服务需要持有昂贵的系统资源。那么可以选择单调服务。
1.2 会话服务
这种激活模式非常类似于传统客户端-服务器模型:当客户端创建代理(new的时候)时会获得一个新建的专有的服务实例,该实例与其他实例无关。该实例将一直保存在服务中直到客户端不再需要它。
[ServiceContract]
public interface IMember
{ ... }
[ServiceBehavior(InstanceContextMode=InstanceContextMode. PerSession)]
public class MemberService : IMember
{ ... }
注:InstanceContextMode的默认值为PerSession,所以以下定义和上面是等效的:
[ServiceContract]
public interface IMember
{ ... }
[ServiceBehavior] //省略不写仍然和上面等效
public class MemberService : IMember
{ ... }
为了关联从特定的客户端发送的特定实例的消息,WCF需要具有识别客户端的能力。为了达到这个目的我们需要通过契约来实现跨越服务边界。
[ServiceContract(SessionMode=SessionMode.Allowed)]
public interface IMember
{ ... }
[ServiceBehavior(InstanceContextMode=InstanceContextMode. PerSession)]
public class MemberService : IMember
{ ... }
注:SessionMode的Allowed是默认值,所以加不加是等效的。
契约配置为支持传输会话模式,这仅仅是支持,但不是强求,如果服务被配置成单调服务,就会采取单调服务的方式执行。
如果SessionMode被配置为Required那么则要求使用传输层会话,如果绑定没有围成一个传输层会话,就不能为这样的契约配置为SessionMode.Required。这一约束会在装载服务时进行验证。
1.3 单例服务
单例服务是一种共享式的服务。当服务被配置为单例时,所有客户端独自链接到相同的单个知名实例,而不考虑链接到的是服务的哪个终结点。单例服务在宿主创建时创建,在宿主释放时释放。
public interface IMember
{ ... }
[ServiceBehavior(InstanceContextMode=InstanceContextMode. Single)]
public class MemberService : IMember
{ ... }
有时初始化单例模式可能需要一些附加的操作,客户端无法完成这样的工作。那么我们需要在托管时完成这些工作:
public interface IMember
{ ... }
[ServiceBehavior(InstanceContextMode=InstanceContextMode. Single)]
public class MemberService : IMember
{
private string serviceName = string.Empty;
public MemberService(string _serivceName)
{
this.serviceName = _serivceName;
}
}
//Host
MemberService singleton = new MemberService("ServiceName");
using (ServiceHost host = new ServiceHost(singleton))
{ ... }
由于单例服务被共享,那么就必须处理多线程访问的同步问题。如果要处理同步问题就只能保证在同一时间只有一个客户访问单例服务。那么如果一次请求需要消耗1/10s的时间。那么1秒就只能处理10个请求,这对应用程序的吞吐量是个巨大的限制。所以我们应该尽量避免使用它。
二 分步操作(操作顺序控制)
有时候我们的服务希望客户端按照一定的顺序进行访问,先执行步骤A再B再C最后D。或则A必须被第一个执行,其他不限。再或者C不能再最后执行等等,WCF为我们提供这样的机制。由于这样的顺序控制需要告知客户端,所以我们需要将其公布在契约当中。
如果需要设置指定某契约为第一步指定(非必须为第一步,可是最后一步):
[OperationContract(IsInitiating = true)]
bool Login(string username, string password, bool CreateCookie);
如果需要指定某契约为最后一步执行(必须是最后一步):
[OperationContract(IsTerminating = true)]
bool LogOut();
如果启用了分步操作,那么必须启用会话(SessionMode为Reqiored),否在在装载时会抛出异常。
如果IsInitiating设置为true,该值为IsInitiating的默认值,表示该方法可以作为第一个方法被调用,如果该方法没有作为第一个方法被调用那么它将成为一个持续会话的一部分(可以是最后一个被调用)。
如果IsInitiating设置为false且该方法被第一个调用将抛出异常,此方法可作为持续会话的一部分或最后一个会话。
如果IsTerminating设置为true,则该方法被执行后服务器将在该方法返回后释放实例。如果在该方法过后仍然存在对其他的方法的调用将抛出异常。
如果IsTerminating设置为false,该值为IsTerminating的默认值,表示服务器不会再该方法过后释放实例。所以该方法可以最为会话调用的任何一步。
三 限流
如果我们的应用程序在某一个时间段流量突然增大,大到服务器无法承受的地步,那么我们可以考虑进行限流。
WCF允许开发者控制以下几个服务消费参数:
1)并发会话最大数:该值控制启用了可靠会话绑定的会话的最大访问数。
2) 并发调用最大数:该值控制所有服务实例当前正在执行的调用总数。该值通常应保持在最大并发会话数的5%以内。
3) 并发实例最大数:该值控制存活的并发上下文数量。
对App.config配置方法如下:
< serviceThrottling
maxConcurrentSessions ="10000"
maxConcurrentCalls ="300"
maxConcurrentInstances ="100"
/>
</ behavior >