8、消息交换模式缓存
WCF服务的实现是基于消息交换的,消息交换模式一共有三种:请求回复模式、单向模式与双工模式。服务器
请求回复模式很好理解,好比int Add(int num1, int num2)这种方法定义就是典型的请求回复模式,请求者发送两个数字,服务回复一个结果数字。若是采用ref或者out参数,那么在xsd当中,ref参数会做为输入和输出参数,out参数只做为输出参数。在WCF当中void返回值的操做契约其实也是请求响应模式的,由于将返回值改成void,影响的只是回复消息的xsd结构,void返回的是一个空xml元素(P141)。并发
对于一些调用服务记录日志等不要求有响应(即使抛异常也不须要客户端知道)的行为,应该采用单向模式,单向模式只须要在操做契约上添加单向的属性:异步
[OperationContract(IsOneWay=true] void WriteLog(string msg);
单向模式的操做在对应的wsdl当中没有输出节点,这样的操做必须使用void做为返回值,其参数也不可以使用ref和out参数(P144)。函数
最后一类是双工模式,双工模式是在服务端定义接口,由客户端实现这个方法,服务端“回调”客户端的这个方法。这里直接扒书加法的例子,由于这个例子又简单又能说明问题,这个例子当中客户端调用服务端的加法,服务端回调客户端的显示函数。ui
首先定义服务契约:spa
[ServiceContract(Namespace = "http://www.artech.com/", CallbackContract = typeof(ICalculatorCallback))] public interface ICalculator { [OperationContract(IsOneWay = true)] void Add(double x, double y); }
这里定义了CallbackContract属性,须要传入一个接口的名字,这个接口名字就是回调操做契约,既然在这里指明了它是个契约,就无需服务契约标签了,这里之因此采用单向,是为了防止死锁:线程
public interface ICalculatorCallback { [OperationContract(IsOneWay = true)] void DisplayResult(double result, double x, double y); }
契约实现以下:代理
public class CalculatorService : ICalculator { public void Add(double x, double y) { double result = x + y; ICalculatorCallback callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>(); callback.DisplayResult(result, x, y); } }
注意实现的第二行,先从当前操做上下文当中拿到了回调信道,以后调用它的回调方法。
客户端实现以下:
public class CalculatorService : ICalculator { public void Add(double x, double y) { double result = x + y; ICalculatorCallback callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>(); callback.DisplayResult(result, x, y); } }
首先是一个回调函数的实现类,它实现了回调契约,不过老A的例子有些不雅,这里直接引了契约的dll。
而后是客户端的主体:
class Program { static void Main(string[] args) { InstanceContext callback = new InstanceContext(new CalculatorCallbackService()); using (DuplexChannelFactory<ICalculator> channelFactory = new DuplexChannelFactory<ICalculator>(callback, "calculatorservice")) { ICalculator calculator = channelFactory.CreateChannel(); calculator.Add(1, 2); } Console.Read(); } }
这里首先建立了实例上下文,用它和终结点的配置一块儿建立了双工信道工厂,以后经过这个工厂建立信道来实现双工调用(这里不雅同上)。
服务端的配置以下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="exposeExceptionDetail"> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="Artech.WcfServices.Service.CalculatorService" behaviorConfiguration="exposeExceptionDetail"> <endpoint address="http://127.0.0.1:3721/calculatorservice" binding="wsDualHttpBinding" contract="Artech.WcfServices.Service.Interface.ICalculator"/> </service> </services> </system.serviceModel> </configuration>
这里采用了支持双工通讯的wsDualHttpBinding绑定,客户端配置以下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <client> <endpoint name ="calculatorservice" address="http://127.0.0.1:3721/calculatorservice" binding="wsDualHttpBinding" contract="Artech.WcfServices.Service.Interface.ICalculator"/> </client> </system.serviceModel> </configuration>
9、实例与会话
上面了例子里有一个InstanceContext对象, 这个对象就是实例上下文,它是对服务实例的封装,对于一个调用服务的请求,WCF会首先反射服务类型来建立服务实例,并用实例上下文对其进行封装(固然这个实例是带“缓存”的),咱们能够配置必定的规则来释放上下文(P396)。
实例上下文分为三种模式:单调模式、会话模式和单例模式。上下文的模式是服务的行为,与客户端无关,以[ServiceBehavior]的InstanceContextMode属性来设置。下面分别来看一看这三种模式。
单调模式,表示每一次调用服务都会建立一个全新的服务实例和上下文,上下文的生命周期与服务调用自己绑定在一块儿(P402),这种方式能最大限度地发挥资源利用率,避免了资源的闲置和竞争,所以单调模式适合处理大量并发的客户端(P406)。
实现单调模式须要在服务的实现类上增长反射标记:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)] public class CalculatorService : ICalculator
从这里也能看出,服务的实现类并不表明业务逻辑,而是位于业务逻辑之上的一个“隔离层”,它显然属于服务层。
单例模式则走了另外一个极端,这种模式让整个服务器上自始至终只存在一个上下文,它的反射标签是:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
既然只有一个上下文,那么说明同时只能处理一个请求,剩下的请求去排队或者超时。这种模式只能应付不多的客户端,并且仅限于作全局计数这样的操做。若是须要让这个服务异步执行,须要这样写反射标签:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,ConcurrencyMode=ConcurrencyMode.Multiple)]
会话模式则将为每个服务代理生成一个上下文,会话使服务具备识别客户端的能力,因此必定要选用支持会话的绑定(P420),这种模式适合于客户端数量不多的应用。
这种模式的服务契约上面有SessionMode标签,Required对服务的整个调用必须是一个会话,默认值为Allowed,会在适当时机采用会话模式。服务契约含有IsInitiating和IsTerminating两个属性,在客户端调用服务时,必须先调用IsInitiating为true和IsTerminating为false的,做为起始,最终要调用IsInitiating为false而IsTerminating为true的,做为终结,在二者之间能够调用全为false的操做。若是不这样调用会报错。
[ServiceContract(SessionMode=SessionMode.Required)] public interface ICalculator { [OperationContract(IsInitiating=true, IsTerminating=false)] void Reset(); [OperationContract(IsInitiating = false, IsTerminating = false)] void Add(int num); [OperationContract(IsInitiating = false, IsTerminating = true)] int GetResult(); }
服务实现以下,首先服务行为加上了InstanceContextMode=InstanceContextMode.PerSession,并在服务的内部保存了一个叫作result的非静态变量:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)] public class CalculatorService : ICalculator { private int result; public void Reset() { result = 0; } public void Add(int num) { result += num; } public int GetResult() { return result; } }
上面一共提到了InstanceContextMode和SessionMode两个枚举,当采用PerCall单调服务时,不论SessionMode如何,中间结果都不会被保存;采起Single单例服务时,不论SessionMode如何中间结果都会被保存,由于上下文是单例的;采起PerSession会话服务时,只有会话模式为Required和Allowed时,中间结果才会被保存。(P427)一张图说明问题:
10、并发
服务行为的InstanceContextMode表示的是对于一个请求,在服务端搞出几个实例上下文来,那么,ConcurrencyMode则表示同一个服务实例如何同时处理多个并行到来的请求,这些请求可能来自同一个服务代理的并行调用,也可能来自多个服务代理的同时调用。
不过在使用ConcurrencyMode以前,须要先给服务/回调服务加上以下标记:
[ServiceBehavior(UseSynchronizationContext=false)] [CallbackBehavior(UseSynchronizationContext=false)]
这是由于服务操做会自动绑定服务的寄宿线程,为了打破这种线程的亲和性须要禁用同步上下文,不然服务就将是串行执行的,而且是采用同一个线程执行的,就没有什么“并发”可言了。(下P197)
对于并发模式,WCF一样提供了三个可选模式。
Single模式表示一个实例上下文在某时刻只能处理单一请求,也就是说针对某个服务上下文的并发请求会串行执行。
在这种模式下,当并发请求到来时,WCF会对实力上下文进行上锁。
Multiple模式表示一个实力上下文能够同时处理多个请求。
Reentrant(可重入)模式和Single相似,只能同时处理一个请求,然而一旦这个请求处理着一半就去回调客户端了,那么在客户端响应以前,其余的并行请求仍是能够被它处理的。举个不雅的例子,男人和老婆亲热着一半,老婆出去拿东西了,这时在外排队的小三就能够进来,等老婆回来了,须要先等小三出来,本身再进去……
在这种模式下,若是须要服务端对客户端进行回调,那么要么采用OneWay的形式回调,要么就要把服务的并发模式设置为非Single,不然会形成死锁的异常,由于“小三”是会占有“原配”的锁的。(下P182)
要让服务支持并发,须要给服务打上服务行为标签,默认值是Single,一样也能够给CallbackBehavior标签设置并发模式:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single)]
一样,前面提到的实力上下文模式和并发模式也是有3*3=9种组合的。
对于单调模式(PerCall),因为每一个服务调用都使用一个实例上下文,因此根本不存在并发状况,无需设置并发模式,可是对于同一个服务代理,若是须要并行发送请求,则须要手动开启服务代理,不然服务是会串行调用的(P189)。
对于会话模式(PerSession),并发将按照ConcurrencyMode所配置的方式进行处理。
对于单例模式(Single),不论并发请求来自一个仍是多个客户端,若ConcurrencyMode是Single则串行,是Multiple则并行,对Reentrant在回调发生时也是并行的(下P195)。
11、限流
为了防止请求数量过多致使服务器资源耗尽,须要在消息接收和处理系统之间创建一道闸门来限制流量,能够经过服务器端配置给服务添加行为来进行流量控制:
<behavior name="throttlingBehavior"> <serviceThrottling maxConcurrentCalls="16" maxConcurrentInstances="116" maxConcurrentSessions="100"/> </behavior>
三个属性分别为能处理的最大并发消息数量、服务实例上下文最大数量和最大并发会话数量,1六、11六、100分别是它们的默认值,在WCF4.0后,这些值是针对单个CPU而言的(下P204)。