zoukankan      html  css  js  c++  java
  • WCF后续之旅(16): 消息是如何分发到Endpoint的消息筛选(Message Filter)

    在介绍终结点的ListenUriMode时,我们提到了两个特殊的对象ChannelDispatcher和ChannelListener。这两个对象在整个WCF的消息分发系统中具有重要的地位,在这节里,我们对WCF的整个消息分发过程作一个简单的介绍。

    一、连接请求的监听

    当我们通过ServiceHost对某个服务进行寄宿的时候,实际上WCF是在为我们创建一个监听器,并监听来自外界的服务访问请求。我们举一个例子,比如针对服务CalculateService,具有如下的配置:该服务具有基于BasicHttpBinding的三个终结点,他们的地址(逻辑地址)分别为:http://127.0.0.1:9999/calculateservice,http://127.0.0.1:8888/calculateservicehttp://127.0.0.1:7777/calculateservice,而前两个共享同一个ListenUri——http://127.0.0.1:6666/calculateservice。而第三个使用默认的ListenUri(也就是终结点地址)。

       1: <?xml version="1.0" encoding="utf-8" ?>
       2: <configuration>    
       3:     <system.serviceModel>
       4:         <services>
       5:             <service name="Artech.WcfServices.Services.CalculateService">
       6:                 <endpoint address="http://127.0.0.1:9999/calculateservice" binding="basicHttpBinding"
       7:                     contract="Artech.WcfServices.Contracts.ICalculate" listenUri="http://127.0.0.1:6666/calculateservice" />
       8:                 <endpoint address="http://127.0.0.1:8888/calculateservice" binding="basicHttpBinding"
       9:                     contract="Artech.WcfServices.Contracts.ICalculate" listenUri="http://127.0.0.1:6666/calculateservice" />
      10:                 <endpoint address="http://127.0.0.1:7777/calculateservice" binding="basicHttpBinding"
      11:                     contract="Artech.WcfServices.Contracts.ICalculate" />
      12:             </service>
      13:         </services>
      14:     </system.serviceModel>
      15: </configuration> 
      16:  

    当我们通过ServiceHost对该服务进行寄宿的时候,会为该服务创建一个ServiceHost对象。当我们执行ServiceHost的Open方法的时候,WCF会创建两个ChannelDispatcher对象。为什么会是两个ChannelDispatcher对象呢?这是因为ChannelDispatcher是根据实际的监听地址创建的,在本例中,虽然我们为服务创建了三个终结点,由于前两个共享同一个监听地址,所所以针对于服务的ServiceHost对象,具有两个ChannelDispatcher对象与之匹配。对于每个ChannelDispatcher对象而言,他们各自对应一个唯一的ChannelListener对象,ChannelListener具有两个方面的作用,其一是绑定到一个具体的URI,监听来自外界的连接请求,其二就是当检测到请求后,创建信道堆栈(channel stack)接受、处理请求。ServiceHost的Open方法的执行,同时也预示着ChannelListener监听工作的开始。

    由于我们为该服务注册了三个终结点,WCF还会创建3个EndpointDispatcher对象,分别于三个终结点对应。对于服务访问请求的消息,会先被对应的ChannelDispacher(这取决于该消息是从哪个ChannelListener接收到的)接收,ChannelDispacher本身并不会对该消息进行处理,而是为将它转发到对应的EndpointDispatcher上,基于该消息的所有后续处理都叫由EndpointDispatcher进行处理。对于这三个EndpointDispatcher对象,前面两个和第一个ChannelDispatcher匹配(根据实际的监听地址进行匹配)。

    总结一下,一个CalculateService服务,对应着一个ServiceHost对象。该ServiceHost对象有具有两个ChannelDispatcher对象,这两个ChannelDispatcher各自具有一个ChannelListener对象,他们对应的监听地址分别为http://127.0.0.1:6666/calculateservicehttp://127.0.0.1:7777/calculateservice。对于前一个ChannelDispatcher,具有两个与之匹配的EndpointDispatcher对象,后一个具有一个匹配的EndpointDispatcher对象。具体关系如下图所示:

    image

    我们可以通过一个例子在正式这一点,完全针对我们上面提供的配置,我们通过下面的代码将该服务在一个控制台应用中进行寄宿,然后打印出ChannelDispatcher和EndpointDispatcher的关系:

       1: //---------------------------------------------------------------
       2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
       3: //---------------------------------------------------------------
       4: using System.ServiceModel;
       5: using Artech.WcfServices.Services;
       6: using System.Threading;
       7: using System;
       8: using System.ServiceModel.Dispatcher; 
       9:  
      10: namespace Artech.WcfServices.Hosting
      11: {
      12:     class Program
      13:     {
      14:         static void Main(string[] args)
      15:         {
      16:             using (ServiceHost serviceHost = new ServiceHost(typeof(CalculateService)))
      17:             {
      18:                 serviceHost.Open();
      19:                 int i=0; 
      20:  
      21:                 foreach (ChannelDispatcher channelDispatcher in serviceHost.ChannelDispatchers)
      22:                 {
      23:                     Console.WriteLine("ChannelDispatcher {0}: ListenUri: {1}", ++i, channelDispatcher.Listener.Uri);
      24:                     int j = 0;
      25:                     foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
      26:                     {
      27:                         Console.WriteLine("\tEndpointDispatcher {0}: EndpointAddress: {1}", ++j,endpointDispatcher.EndpointAddress.Uri);
      28:                     }
      29:                 }
      30:                 Console.Read();
      31:             }
      32:         }
      33:     }
      34: } 
      35:  

    最终输出的结果印证了我们上面对ServiceHost、ChannelDispatcher、ChannelListener和EndpointDispatcher的关系:

       1: ChannelDispatcher 1: ListenUri: http://127.0.0.1:6666/calculateservice
       2:       EndpointDispatcher 1: EndpointAddress: http://127.0.0.1:9999/calculateservice
       3:       EndpointDispatcher 2: EndpointAddress: http://127.0.0.1:8888/calculateservice
       4: ChannelDispatcher 2: ListenUri: http://127.0.0.1:7777/calculateservice
       5:       EndpointDispatcher 1: EndpointAddress: http://127.0.0.1:7777/calculateservice
    2、EndpointDispatcher的选择和消息的分发

    接着上面的例子,当服务被成功寄宿之后,两个ChannelDispatcher的ChannelListener便开始在各自的监听URI上进行监听。一旦某个服务调用请求被某个ChannelListener监测到,ChannelListner会调用AcceptChannel方法创建信道栈(channel stack)接收和处理请求消息。

    当消息被接收信道栈处理完毕之后,ChannelListener所在的ChannelDispatcher需要将消息分发给对应的EndpointDispatcher。但是对于一个ChannelDiaptcher对应多个EndpointDispatcher的情况,究竟该如何选择适合的EndpointDispatcher呢?EndpointDispatcher的选择依赖于两个特殊的MessageFilter——AddressFilter和ContractFilter。

       1: //---------------------------------------------------------------
       2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
       3: //---------------------------------------------------------------
       4: public class EndpointDispatcher
       5: {
       6:     //... ...
       7:     public MessageFilter AddressFilter { get; set; }
       8:     public MessageFilter ContractFilter { get; set; }
       9: } 
      10:  

    我们先来看看MessageFilter的定义, 如下所示,MessageFilter类定义两个重载的Match方法,参数分别是Message和MessageBuffer。当MessageFilter的Match方法返回True,就表明该MessageFilter对象对应的EndpointDispatcher正是真正被请求的EndpointDispatcher。也就是说当ChannelDispatcher进行筛选的时候,会遍历它所有的EndpointDispatcher,获取他们的AddressFilter和ContractFilter,调用Match方法,如果两者都返回true,则表明是真正的需要的EndpointDispatcher。

       1: //---------------------------------------------------------------
       2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
       3: //---------------------------------------------------------------
       4: public abstract class MessageFilter
       5: {
       6:     public abstract bool Match(Message message);
       7:     public abstract bool Match(MessageBuffer buffer);
       8: } 
       9:  

    WCF定义了6种MessageFilter:ActionMessageFilter、EndpointAddressMessageFilter、XPathMessageFilter、PrefixEndpointAddressMessageFilter、MatchAllMessageFilter和MatchNoneMessageFilter。如下面的类图所示,这6种MessageFilter均继承自抽象类:System.ServiceModel.Dispatcher.MessageFilter。

    • ActionMessageFilter:对于服务契约的每个操作都具有一个Action,可以是显示指定的,也可以是默认的(服务契约的命名空间+操作名称),也就是说一个终结点的具有一个Action列表。在进行筛选的时候,如果SOAP消息的Action报头的值存在于终结点的Action列表中,则匹配成功
    • EndpointAddressMessageFilter:如果SOAP消息的To报头和终结点的地址完全一样,则匹配成功
    • XPathMessageFilter:SOAP消息也是一个XML,所以可以根据一个具体的XPath表达式和SOAP的内容进行匹配
    • PrefixEndpointAddressMessageFilter:和EndpointAddressMessageFilter一样,也是通过SOAP消息的To报头和终结点的地址进行比较,不过这里仅仅比较地址的前缀
    • MatchAllMessageFilter:不管消息的内容是什么,都会匹配成功
    • MatchNoneMessageFilter:和MatchAllMessageFilter相反,不管消息的内容是什么,都不会匹配成功

    MessageFilter

    在默认的情况下,EndpointDispatcher的AddressFilter采用的是EndpointAddressMessageFilter,而ContractFilter采用的是ActionMessageFilter。如果希望改变EndpointDispatcher的AddressFilter和ContractFilter的值,你可以通过自定义Behavior的形式覆盖掉默认的值。对于AddressFilter,你有一种最直接的方式,通过ServiceBehaviorAttribute的AddressFilterMode属性指定你所需要的MessageFilter。

       1: //---------------------------------------------------------------
       2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
       3: //---------------------------------------------------------------
       4: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
       5: {
       6:     //... ...
       7:     public AddressFilterMode AddressFilterMode { get; set; }
       8: }
       9: public enum AddressFilterMode
      10: {
      11:     Exact,
      12:     Prefix,
      13:     Any
      14: } 

    其中Exact对应EndpointAddressMessageFilter;Prefix对应PrefixEndpointAddressMessageFilter;Any对应MatchAllMessageFilter。比如通过下面的代码,将AddressFilter指定为MatchAllMessageFilter:

       1: //---------------------------------------------------------------
       2: // EndpointAddress & WCF Addressing (c) by 2008 Jiang Jin Nan
       3: //---------------------------------------------------------------
       4: [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
       5: public class CalculateService:ICalculate
       6: {
       7:     ...
       8: }

    WCF后续之旅: 
    WCF后续之旅(1): WCF是如何通过Binding进行通信的 
    WCF后续之旅(2): 如何对Channel Layer进行扩展——创建自定义Channel 
    WCF后续之旅(3): WCF Service Mode Layer 的中枢—Dispatcher 
    WCF后续之旅(4):WCF Extension Point 概览 
    WCF后续之旅(5): 通过WCF Extension实现Localization 
    WCF后续之旅(6): 通过WCF Extension实现Context信息的传递 
    WCF后续之旅(7):通过WCF Extension实现和Enterprise Library Unity Container的集成 
    WCF后续之旅(8):通过WCF Extension 实现与MS Enterprise Library Policy Injection Application Block 的集成 
    WCF后续之旅(9):通过WCF的双向通信实现Session管理[Part I] 
    WCF后续之旅(9): 通过WCF双向通信实现Session管理[Part II] 
    WCF后续之旅(10): 通过WCF Extension实现以对象池的方式创建Service Instance 
    WCF后续之旅(11): 关于并发、回调的线程关联性(Thread Affinity) 
    WCF后续之旅(12): 线程关联性(Thread Affinity)对WCF并发访问的影响 
    WCF后续之旅(13): 创建一个简单的WCF SOAP Message拦截、转发工具[上篇] 
    WCF后续之旅(13):创建一个简单的SOAP Message拦截、转发工具[下篇] 
    WCF后续之旅(14):TCP端口共享 
    WCF后续之旅(15): 逻辑地址和物理地址 
    WCF后续之旅(16): 消息是如何分发到Endpoint的--消息筛选(Message Filter) 
    WCF后续之旅(17):通过tcpTracer进行消息的路由


    作者:Artech
    出处:http://artech.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    C语言 realloc为什么要有返回值,realloc返回值具体解释/(解决随意长度字符串输入问题)。
    opencv中的vs框架中的Blob Tracking Tests的中文注释。
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 棋盘多项式
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
    Java实现 蓝桥杯VIP 算法提高 分苹果
  • 原文地址:https://www.cnblogs.com/artech/p/1293185.html
Copyright © 2011-2022 走看看