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)。