[WCF 4.0新特性] 路由服務[原理篇]
在一個典型的服務調用場景中,具有兩個基本的角色,即服務的消費者和服務的提供者。從消息交換的角度講前者一般是消息的最初發送者,而後者則是消息的最終接收者。在很多情況下,由於網絡環境的局限,消息的最初發送者和最終接收者不能直接進行消息交換,這就需要一個輔助實現消息路由的中介服務,這就是我們接下來要介紹的路由服務。
目錄
一、路由服務就是一個WCF服務
路由服務契約的定義
路由服務契約的定義
二、基於消息內容的路由策略
RoutingBehavior服務行為
消息篩選器
篩選器表
路具體的消息路由操作實現在該服務的某個操作之中。和其他一般的服務比較,並沒有太多不同之處。在使用路由服務之前,我們也需要像一般的服務一樣對其進行寄宿,並為之指定一個基於某種綁定的終結點。對於需要被路由的服務的客戶端,除了需要將路由服務的地址作為其消息發送的物理地址之外,它依然像普通的方式一樣對目標服務進行調用。
路由服務的定義
既然路由服務本質上就是一個WCF服務,它肯定就對應著某個實現相應服務契約的類型,這個類型就是RoutingService。下麵的代碼片斷給出了RoutingService的成員定義。
1: [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
2: [ServiceBehavior(AddressFilterMode=AddressFilterMode.Any,
3: InstanceContextMode=InstanceContextMode.PerSession,
4: UseSynchronizationContext=false,
5: ValidateMustUnderstand=false)]
6: public sealed class RoutingService : ISimplexDatagramRouter,
7: ISimplexSessionRouter,
8: IRequestReplyRouter,
9: IDuplexSessionRouter,
10: IDisposable
11: {
12: //ISimplexDatagramRouter
13: [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
14: IAsyncResult ISimplexDatagramRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state);
15: void ISimplexDatagramRouter.EndProcessMessage(IAsyncResult result);
16:
17: //ISimplexSessionRouter
18: [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
19: IAsyncResult ISimplexSessionRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state);
20: void ISimplexSessionRouter.EndProcessMessage(IAsyncResult result);
21:
22: //IRequestReplyRouter
23: [OperationBehavior(Impersonation = ImpersonationOption.Allowed)]
24: IAsyncResult IRequestReplyRouter.BeginProcessRequest(Message message, AsyncCallback callback, object state);
25: Message IRequestReplyRouter.EndProcessRequest(IAsyncResult result);
26:
27: //IDuplexSessionRouter
28: [OperationBehavior(Impersonation=ImpersonationOption.Allowed)]
29: IAsyncResult IDuplexSessionRouter.BeginProcessMessage(Message message, AsyncCallback callback, object state);
30: void IDuplexSessionRouter.EndProcessMessage(IAsyncResult result);
31:
32: void IDisposable.Dispose();
33: }
我們根據其定義來簡單地分析一下表示路由服務的RoutingService。首先,RoutingService實現了四個服務契約接口,分別是ISimplexDatagramRouter、ISimplexSessionRouter、IRequestReplyRouter和IDuplexSessionRouter。在這裏我們從RoutingService對它們的實現不難看出,這四個契約接口以異步模式定義了一個唯一的操作。其中定義在IRequestReplyRouter中的叫做ProcessRequest,定義在其他三個的則叫做ProcessMessage。實際上ProcessRequest/ProcessMessage就是真正實現消息路由的服務操作。
其次,RoutingService分別用於實現該操作方法的BeginProcessMessage方法上應用了OperationBehaviorAttribute特性並將Impersonation設置味著允許身份模擬(關於身份模式,可以參閱《模擬在WCF中的應用》)。
最後,在類型級別應用了AspNetCompatibilityRequirementsAttribute特性並將RequirementsMode屬性設置為AspNetCompatibilityRequirementsMode.Allowed,意味著路由服務允許與ASP.NET兼容(關於ASP.NET兼容模式請參閱《WCF技術剖析之四:基於IIS的WCF服務寄宿(Hosting)實現揭秘》)。同時應用了ServiceBehaviorAttribute特性<To>報頭)與本終結點不一致的請求消息。而在很多情況下,請求消息攜帶的目標地址一般都是目標服務的地址,而不是作為中介的路由服務的地址。
路由服務契約的定義
RoutingService顯式實現的四個服務契約接口具有相似的操作方法的定義,其主要的目的在於對不同消息交換
1: [ServiceContract(Namespace = "...",
2: SessionMode = SessionMode.Allowed)]
3: public interface ISimplexDatagramRouter
4: {
5: [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
6: IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, object state);
7: void EndProcessMessage(IAsyncResult result);
8: }
9: [ServiceContract(Namespace = "...",
10: SessionMode = SessionMode.Required)]
11: public interface ISimplexSessionRouter
12: {
13: [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
14: IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, object state);
15: void EndProcessMessage(IAsyncResult result);
16: }
17: [ServiceContract(Namespace = "...",
18: SessionMode = SessionMode.Allowed)]
19: public interface IRequestReplyRouter
20: {
21: [GenericTransactionFlow(TransactionFlowOption.Allowed)]
22: [OperationContract(AsyncPattern = true, IsOneWay = false, Action = "*", ReplyAction = "*")]
23: IAsyncResult BeginProcessRequest(Message message, AsyncCallback callback, object state);
24: Message EndProcessRequest(IAsyncResult result);
25: }
26: [ServiceContract(Namespace = "...",
27: SessionMode = SessionMode.Required, CallbackContract = typeof(IDuplexRouterCallback))]
28: public interface IDuplexSessionRouter
29: {
30: [GenericTransactionFlow(TransactionFlowOption.Allowed)]
31: [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]
32: IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback, object state);
33: void EndProcessMessage(IAsyncResult result);
34: }
四個不同的路由服務契約實際上均定義一個唯一的實現消息路由的操作ProcessMessage/ ProcessRequest操作。隻不過這個操作是以
路由服務以一個“中介服務”的形式提供消息路由的功能。具體來說,服務的客戶端將於原本應該發送給目標服務的基於某個操作的調用消息轉發給路由服務,而路由服務將接收到的消息作為輸入,轉而調用目標服務。路由服務對消息的接收和轉發機製如下圖所示。
當路由服務接收到請求消息選擇目標服務的時候,隻需按照先後順序或者優先級別遍曆篩選器表中的每個消息篩選器,並以請求消息作為輸入調用之,直到返回結構為True。而最終的這個消息篩選器對應的終結點就指向本次理由請求的目標服務。路由服務采用的消息篩選機製大體上上圖所示。
RoutingBehavior服務行為
上麵介紹的這套基於消息消息篩選的終結點選擇機製最終是通過服務行為RoutingBehavior來實現的,所以我們寄宿路由服務RoutingService的時候,除了為終結點選擇適合的綁定和滿足相應消息交換模式的服務契約之外,還需在此服務上應用RoutingBehavior服務行為。下麵的代碼片斷顯示了RoutingBehavior類型的主要成員定義。
1: public sealed class RoutingBehavior : IServiceBehavior
2: {
3: //其他成員
4: public RoutingBehavior(RoutingConfiguration routingConfiguration);
5: void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters);
6: void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
7: void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
8: }
從RoutingBehavior的定義我們可以看出,在調用構造函數創建RoutingBehavior的時候,需要指定一個RoutingConfiguration對象作為其參數。從其類型名稱,我們知道該類是對路由策略信息的封裝,下麵的代碼片斷列出了RoutingConfiguration的核心屬性的定義。
上麵我們已經介紹過了,路由服務采用基於消息篩選機製的路由策略,而整個路由策略的實施依賴一個篩選器表,作為核心的篩選器表通過RoutingConfiguration的屬性FilterTable表示。此外,RoutingConfiguration還具有兩個布爾類型的屬性SoapProcessingEnabled和RouteOnHeadersOnly。前者表示是否按照SOAP消息的方式進行路由處理,而後者則表式路由的處理是否僅僅需要使用到報頭信息。SoapProcessingEnabled和RouteOnHeadersOnly兩屬性默認為True。
1: public sealed class RoutingConfiguration
2: {
3: //其他成員
4: public MessageFilterTable<IEnumerable<ServiceEndpoint>> FilterTable { get; }
5: public bool RouteOnHeadersOnly { get; set; }
6: public bool SoapProcessingEnabled { get; set; }
7: }
在大部分情況下,我們通過配置的方式將RoutingBehavior行為應用到路由服務上,該行為對應的行為配置元素名稱為<routing>。在下麵的配置中,我為寄宿的路由服務RoutingService添加了一個基於WS2007HttpBinding的終結點,該終結點采用IRequestReplyRouter作為其服務契約。該服務應用了一個名稱為routingBehavior的服務行為,而RoutingBehavior行為的配置就包含其中。<routing>配置元素的routeOnHeadersOnly和soapProcessingEnabled分別對應著RoutingConfiguration的同名屬性,而filterTableName則表示配置的篩選器表的名稱。
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <behaviors>
5: <serviceBehaviors>
6: <behavior name="routingBehavior">
7: <routing filterTableName="greetingFilterTable" routeOnHeadersOnly="true" soapProcessingEnabled="true" />
8: </behavior>
9: </serviceBehaviors>
10: </behaviors>
11: <services>
12: <service behaviorConfiguration="routingBehavior" name="System.ServiceModel.Routing.RoutingService">
13: <endpoint binding="ws2007HttpBinding" contract="System.ServiceModel.Routing.IRequestReplyRouter" />
14: </service>
15: </services>
16: </system.serviceModel>
17: ...
18: </configuration>
接下來我們會具體地介紹RoutingBehavior配置中引用的篩選器表,以及具體的篩選器如何進行配置。
消息篩選器
對於WCF來說,消息篩選器旨在實現對給定消息的評估以判斷它是否滿足某個預先指定的條件,比如消息攜帶的AS-Addressing報頭是否和預先指定的一致。WCF中所有的消息篩選器都繼承自抽象類MessageFilter,而具體的消息評估操作實現在兩個Match方法中。
1: public abstract class MessageFilter
2: {
3: //其他成員
4: public abstract bool Match(Message message);
5: public abstract bool Match(MessageBuffer buffer);
6: }
WCF定義了一係列的係統定義的消息篩選器,如果我們在消息篩選過程中需要特殊的消息評估邏輯,我們還可以通過繼承這個MessageFilter抽象類自定義消息篩選器。 我們經常使用的包括如下的六種:
- ActionMessageFilter:該篩選器包含一組預先指定的表示Action的字符串,判斷給定的消息的WS-Addressing <Action>報頭是否是其中之一;
- EndpointAddressMessageFilter:預先指定一個EndpoingAddress對象,判斷給定消息的WS-Addressing <To>報頭的值是否與之匹配;
- PrefixEndpointAddressMessageFilter:預先指定一個EndpoingAddress對象,判斷給定消息的WS-Addressing <To>報頭的值於指定的地址是否具有相同的前綴;
- EndpointNameMessageFilter:預先指定一個表示終結點名稱的字符串,判斷給定消息的是否具有一個名稱為“System.ServiceModel.Routing.EndpointNameMessageFilter.Name”的屬性,並且屬性值與指定的值一致;
- XPathMessageFilter:預先指定一個XPath格式的字符串,判斷表示消息的XML是否滿足基於該XPath的查詢;
- MatchAllMessageFilter:不管消息的內容是什麼,都會匹配成功。
此外,WCF還為我們定義了一個特殊的消息篩選器StrictAndMessageFilter。它本身並不進行具體的消息評估的工作,具體的消息評估工作由它所包含的兩個具體的消息篩選器來完成。隻要當這兩個消息篩選器評估結構均返回True時,該StrictAndMessageFilter才返回True,否則返回False。
供路由服務使用的所有消息篩選器均配置在WCF配置節的<routing>/<filters>節點下。該節點下表示單個消息篩選器配置元素的<filter>具有三個基本的配置屬性:name、filterType和filterData,分別表示消息篩選器的名稱、類型和創建篩選器需要的參數信息。為對於上述的六種消息篩選器,WCF為它們的類型分別定義了別名,分別是Action、EndpointAddress、Endpoint、XPath和MatchAll。在進行配置的時候隻需要對filterType屬性設置相應的類型別名即可。在下麵配置片斷中,我定義了6中消息篩選器,它們分別對應著上述的6種類型。
1: <configuration>
2: <system.serviceModel>
3: <routing>
4: <filters>
5: <filter name="ActionMessageFilter1" filterType="Action" filterData="https://namespace/contract/operation" />
6: <filter name="EndpointAddressMessageFilter1" filterType="EndpointAddress" filterData="https://host/vdir/s.svc/b" />
7: <filter name="PrefixEndpointAddressMessageFilter1" filterType="EndpointAddressPrefix" filterData="https://host/" />
8: <filter name="EndpointNameMessageFilter" filterType="Endpoint" filterData="SvcEndpoint" />
9: <filter name="XPathMessageFilter1" filterType="XPath" filterData="//ns:element" />
10: <filter name="MatchAllMessageFilter1" filterType="MatchAll" />
11: </filters>
12: <routing>
13: </system.serviceModel>
14: </configuration>
對於StrictAndMessageFilter的配置來說,它也具有相應的類型別名And。由於它是通過額外的兩個消息篩選器來完成具體的消息評估,在配置中這兩個消息篩選器通過屬性filter1和filter2來表示。在下麵的配置中,我們定義了三個消息篩選器,而前兩個是為第三個StrictAndMessageFilter1服務的。
1: <configuration>
2: <system.serviceModel>
3: <routing>
4: <filters>
5: <filter name="filter1" ... />
6: <filter name="filter2" ... />
7: <filter name="StrictAndMessageFilter1" filterType="And" filter1="filter1" filter2="filter2" />
8: </filters>
9: <routing>
10: </system.serviceModel>
11: </configuration>
上麵介紹的都是係統定義的消息篩選器的配置,對於自定義的消息篩選器又該是如何配置呢?比如說,我定義了如下一個MyMessageFilger,並且它具有包含兩個字符串參數的構造函數。
1: namespace Artech.CustomMessageFilters
2: {
3: public class MyMessageFilger : MessageFilter
4: {
5: public MyMessageFilger(string arg1, string arg2)
6: {
7: //省略實現
8: }
9: public override bool Match(Message message)
10: {
11: //省略實現
12: }
13: public override bool Match(MessageBuffer buffer)
14: {
15: //省略實現
16: }
17: }
18: }
那麼我們需要通過下麵的方式對這個自定義消息篩選器進行配置。首先需要將filterType設置為“Custom”,並通過customType指定消息篩選器的類型,傳入構造函數的參數通過filterData進行設置,參數值通過逗號分隔。
1: <configuration>
2: <system.serviceModel>
3: <routing>
4: <filters>
5: <filter name="MyMessageFilter1" filterType="Custom"
6: customType="Artech.CustomMessageFilters.MyMessageFilter, Artech.CustomMessageFilters"
7: filterData="argValue1, argValue2"/>
8: </filters>
9: <routing>
10: </system.serviceModel>
11: </configuration>
篩選器表
上麵配置的具名消息篩選器最終是為了創建用於定義路由策略的篩選器表服務的。篩選器表配置在<routing>/<filterTables>配置節下,表示具體篩選器配置元素的<filterTable>具有一個必須的配置屬性name表示篩選器的名稱,而之前我們介紹的配置在RoutingBehavior服務行為上的篩選器表名就是指的這個。
<filterTable>下的<add>節點表示具體的消息篩選器和指向目標服務的客戶端中節點之間的映射關係。其中filterName和endpointName屬性是對配置的消息篩選器和客戶端終結點的引用。除此之外,我們還可以通過可選的priority屬性配置匹配的級別。篩選器表的具體配置可以參考下麵
1: <configuration>
2: <system.serviceModel>
3: <client>
4: <endpoint name="endpoint1" .../>
5: endpoint name="endpoint2" .../>
6: </client>
7: <routing>
8: <filters>
9: <filter name ="filter1" .../>
10: <filter name ="filter2" .../>
11: </filters>
12: <filterTables>
13: <filterTable name="myFilterTable">
14: <add filterName="filter1" endpointName="endpoint1" priority="" />
15: <add filterName="filter2" endpointName="endpoint2" priority=""/>
16: </filterTable>
17: </filterTables>
18: </routing>
19: </system.serviceModel>
20: </configuration>
[WCF 4.0新特性] 路由服務[原理篇]
[WCF 4.0新特性] 路由服務[實例篇]
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 15:33:57