- WCF技術剖析(卷1)
- 蔣金楠
- 1517字
- 2018-12-27 11:32:46
2.3.4 消息篩選
在上面的案例演示中,提到了兩個特殊的對象ChannelDispatcher和ChannelListener。這兩個對象在整個WCF的消息分發系統中具有重要的地位,在這節里,我們對WCF的整個消息分發過程作一個簡單的介紹。
連接請求的監聽
當服務被成功寄宿時,WCF在服務端創建一個或多個監聽器,用于服務調用請求的監聽。舉個例子,我們為服務CalculatorService添加三個基于BasicHttpBinding的終結點,它們分別具有如下3個終結點地址(邏輯地址)。
● http://127.0.0.1:9999/CalculatorService
● http://127.0.0.1:8888/CalculatorService
● http://127.0.0.1:7777/CalculatorService
我們為前兩個終結點指定一個不同于終結點地址的監聽地址(物理地址):http://127.0.0.1:6666/CalculatorService。第三個終結點的監聽地址(物理地址)與終結點地址(邏輯地址)共享相同的URI:http://127.0.0.1:7777/CalculatorService。相應的配置如下:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="Artech.WcfServices.Services.CalculatorService"> <endpoint address="http://127.0.0.1:9999/CalculatorService" binding="basicHttpBinding" contract="Artech.WcfServices.Contracts.ICalculator" listenUri="http://127.0.0.1:6666/CalculatorService" /> <endpoint address="http://127.0.0.1:8888/CalculatorService" binding="basicHttpBinding" contract="Artech.WcfServices.Contracts.ICalculator" listenUri="http://127.0.0.1:6666/CalculatorService" /> <endpoint address="http://127.0.0.1:7777/CalculatorService" binding="basicHttpBinding" contract="Artech.WcfServices.Contracts.ICalculator" /> </service> </services> </system.serviceModel> </configuration>
現在的情況是,1個服務、3個終結點、2個監聽地址。當服務被成功寄宿時,WCF會創建兩個信道分發器(ChannelDispatcher)對象,每個信道分發器各擁有屬于自己的信道監聽器(ChannelListener),它們分別綁定到兩個監聽地址對應的端口進行服務調用請求的監聽。此外,WCF還會為服務的3個終結點創建3個終結點分發器(EndpointDispatcher),當信道分發器通過信道監聽器接收到消息時,將會根據消息自身攜帶的信息選擇與之相匹配的終結點分發器,根據消息進行終結點分發器的選擇機制稱為消息篩選(Message Filter)。關于上面介紹的場景,圖2-9體現了基于CalculatorService對應的ServiceHost、ChannelDispatcher和EndpointDispatcher之間的關系。

圖2-9 ServiceHost-ChannelDispatcher-EndpointDispatcher
圖2-9揭示的圍繞CalculatorService的各個相關對象之間的關系也可以從下面的程序得以印證。
using System.ServiceModel; using Artech.WcfServices.Services; using System.Threading; using System; using System.ServiceModel.Dispatcher; namespace Artech.WcfServices.Hosting { class Program
{ static void Main(string[] args) { using (ServiceHost serviceHost = new ServiceHost (typeof(CalculatorService))) { serviceHost.Open(); int i=0; foreach (ChannelDispatcher channelDispatcher in serviceHost. ChannelDispatchers) { Console.WriteLine("ChannelDispatcher {0}: ListenUri: {1}", ++i, channelDispatcher.Listener.Uri); int j = 0; foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) { Console.WriteLine("\tEndpointDispatcher {0}: EndpointAddress: {1}", ++j,endpointDispatcher. EndpointAddress.Uri); } } Console.Read(); } } } }
輸出結果:
ChannelDispatcher 1: ListenUri: http://127.0.0.1:6666/CalculatorService EndpointDispatcher 1: EndpointAddress: http://127.0.0.1:9999/ CalculatorService EndpointDispatcher 2: EndpointAddress: http://127.0.0.1:8888/ CalculatorService ChannelDispatcher 2: ListenUri: http://127.0.0.1:7777/CalculatorService EndpointDispatcher 1: EndpointAddress: http://127.0.0.1:7777/ CalculatorService
EndpointDispatcher的選擇和消息的分發
在WCF整個消息監聽與分發體系中,信道分發器和終結點分發器是兩個核心的對象。信道分發器進行請求監聽和消息接收,終結點分發器最終完成對消息的處理。信道分發器接收的消息最終需要分發給相應的終結點分發器,而消息篩選解決了對終結點的選擇問題。消息篩選依賴于終結點分發器兩個重要的對象:AddressFilter和Contractfilter,它們分別實現基于終結點地址和服務契約的消息篩選。
public class EndpointDispatcher { //其他成員 public MessageFilter AddressFilter { get; set; } public MessageFilter ContractFilter { get; set; } }
AddressFilter和Contractfilter具有相同的類型:System.ServiceModel.Dispatcher. MessageFilter。MessageFilter是一個抽象類型,定義了兩個重載的Match方法,用以判斷持有該MessageFilter的EndpointDispatcher是否和接收的消息相匹配。
public abstract class MessageFilter { public abstract bool Match(Message message); public abstract bool Match(MessageBuffer buffer); }
當ChannelDispatcher接收到請求消息時,會遍歷屬于自己的EndpointDispatcher列表,獲取它們的兩個MessageFilter:AddressFilter和Contractfilter,并將消息對象傳入Match方法。如果返回值均為true,則意味著相應的EndpointDispatcher為真正的目標終結點。
WCF定義了以下6種不同的MessageFilter類型,它們均直接繼承自MessageFilter抽象類型。
● ActionMessageFilter:每一個服務操作具有一個Action屬性,通過OperationContractAttribute特性進行定義。一個服務契約包含一個或多個服務操作,所以一個終結點具有一組Action列表。AddressMessageFilter通過判斷SOAP消息的Action報頭的值是否在終結點Action列表之中,從而選擇正確的終結點;
● EndpointAddressMessageFilter:EndpointAddress是一個終結點不可或缺的元素,EndpointAddress不僅包含服務的地址,也包含尋址的報頭(AddressHeader),能夠通過EndpointAddressMessageFilter篩選的終結點需要同時滿足兩個要求:終結點地址URI要與SOAP的To報頭值一致;SOAP消息具一致的報頭信息;
● XPathMessageFilter:SOAP消息也是一個XML,所以可以根據一個具體的XPath表達式和SOAP的內容進行匹配;
● PrefixEndpointAddressMessageFilter:和EndpointAddressMessageFilter篩選機制類似,不同的是PrefixEndpointAddressMessageFilter采用“最長前綴匹配”機制。比如,終結點地址指定的URI為http://www.artech.com/Foo,而請求消息的To報頭的URI為http://www.artech.com/Foo/Bar,這樣可以被認為是匹配的;
● MatchAllMessageFilter:不管消息的內容是什么,都會匹配成功;
● MatchNoneMessageFilter:和MatchAllMessageFilter相反,不管消息的內容是什么,都不會匹配成功。
在默認情況下,EndpointDispatcher的AddressFilter和ContractFilter分別采用EndpointAddressMessageFilter和ActionMessageFilter。如果希望使用其他的值,可以通過自定義Behavior的形式覆蓋默認的值。對于AddressFilter,最直接的方式就是通過ServiceBehaviorAttribute的AddressFilterMode屬性指定你所需要的MessageFilter模式。
public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior { //其他成員 public AddressFilterMode AddressFilterMode { get; set; } }
public enum AddressFilterMode { Exact, Prefix, Any }
AddressFilterMode中的3個枚舉值(Exact、Prefix和Any)對應的MessageFilter分別為:EndpointAddressMessageFilter、PrefixEndpointAddressMessageFilter和MatchAllMessageFilter。下面的代碼將AddressFilter指定為MatchAllMessageFilter。
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)] public class CalculatorService:ICalculator { //省略成員 }