WCF技术剖析(卷1)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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
        {
            //省略成员
        }