WCF4.0 —— Routing Service

现在WCF 4.0内置了路由服务——System.ServiceModel.Routing.RoutingService,可以在 System.ServiceModel.Routing.dll 中找到。
比如下面的场景会使用到路由服务:只暴露一个外部公开的 Endpoint 映射到内部的多个的服务上。
WCF4.0 —— Routing Service_第1张图片

路由服务使用的消息筛选器提供常用消息选择功能,例如,终结点的名称、SOAP 操作或消息已发送到的地址或地址前缀。也可以使用 AND 条件连接筛选器,这样,仅当消息同时与两个筛选器匹配时,才会将消息路由至某个终结点。此外,还可以通过创建自己的 MessageFilter 实现来创建自定义筛选器。(MSDN:消息筛选器)

OK,废话少说下面做一个实例来看看 RoutingService 该如何配置。
1. Console Hosting Request/Reply WCF Service Routing (示例代码下载)
(1) 创建一个普通的Wcf Service Library (使用Console Hosting)
Console Host

static void Main(string[] args)
{
    using (var host = new ServiceHost(typeof(WcfSvcLib.Service1)))
    {
        host.Open();
        Console.WriteLine("WcfService1 is started...............");
        Console.Read();
    }
}
app.config
<system.serviceModel>
  <services>
    <service name="WcfSvcLib.Service1" behaviorConfiguration="WcfSvcLib.Service1Behavior">
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:20000/WcfRoutingServiceTest/Service1/"/>
        </baseAddresses>
      </host>
      <endpoint name="WcfService1" address="" binding="wsHttpBinding" contract="WcfSvcLib.IService1">
        <identity>
          <dns value="localhost"/>
        </identity>
      </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
    </service>
  </services>

  <behaviors>
    <serviceBehaviors>
      <behavior name="WcfSvcLib.Service1Behavior">
        <serviceMetadata httpGetEnabled="True"/>
        <serviceDebug includeExceptionDetailInFaults="False"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>    
</system.serviceModel>

为了测试,Service1 使用的是 PerSession,当 Service1 被第一次调用时,服务端构造一次 Service1 实例。并在之后的调用中保持该 Session。
SetName 将保存一个值,GetName 则取出该值。

namespace WcfSvcLib
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class Service1 : IService1
    {

        private string _name = "";

        public Service1()
        {
            Console.WriteLine("Service1 ctor: " + Guid.NewGuid().ToString());
        }

        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }

        public void SetName(string name)
        {
            _name = name;
        }

        public string GetName()
        {
            return _name;
        }
    }
}
(2) 创建 Wcf Routing Service Host (使用Console Hosting)
首先添加 System.ServiceModel.Routing.dll,  using System.ServiceModel.Routing; 
static void Main(string[] args)
{
    using (var host = new ServiceHost(typeof(RoutingService)))
    {
        try
        {
            host.Open();
            Console.WriteLine("RoutingService is started...............");
            Console.Read();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        Console.Read();
    }
}

重点在于 RoutingService 的配置文件,详细参看代码中的注释说明。

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true"/>
  </system.web>
  
  <system.serviceModel>
    <services>
      <!-- 定义 Routing Service 的地址(baseAddress+endpoint address),
             绑定(wsHttpBinding),契约(System.ServiceModel.Routing.IRequestReplyRouter) -->
      <!-- 注意:Routing Service 的契约必须和被路由服务的模型吻合比如: 
             Request/Reply 模式,OneWay 模式,或者是 Duplex 模式。这里是 Request/Reply 模式 -->
      <service name="System.ServiceModel.Routing.RoutingService" behaviorConfiguration="MyRoutedServBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:20002/WcfRoutingServiceTest/router"/>
          </baseAddresses>
        </host>
        <endpoint address="service1" binding="wsHttpBinding" name="MyRoutingEndpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter"/>
      </service>
    </services>
  
    <!-- 在 serviceBehavior 中加入了 routing 配置节,指明使用的 filterTableName -->
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyRoutedServBehavior">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="True"/>
          <routing filterTableName="ServiceRouterTable"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <routing>
      <!-- filterTable 中配置了 filter 和 client endpoint 的映射关系 -->
      <!-- 注意:当使用 Request/Reply 模型时,filterTable 里不能有多个 Endpoint 
          (详细参看:http://msdn.microsoft.com/zh-cn/library/ee816891.aspx 的多播一节)-->
      <filterTables>
        <filterTable name="ServiceRouterTable">
          <add filterName="ServiceRouter_Filter1" endpointName="WcfService1"/>
          <!--
          <add filterName="ServiceRouter_Filter2" endpointName="WcfService1"/>
          <add filterName="ServiceRouter_Filter3" endpointName="WcfService1"/>
          -->
        </filterTable>
      </filterTables>
      
      <filters>
        <!-- 配置 filter:-->
        <!-- filterType 有:MatchAll, Action, EndpointAddress, EndpointAddressPrefix, EndpointName, XPath, Custom, And-->
        <!-- (1) MatchAll 时,filterData 就无视了,直接转发给真实的 Endpoint -->
        <!-- (2) Action 时,filterData 里填写的内容应该是 "协议://Namespace(注1)/ServiceContractName/OperationContractName" -->
        <!-- (3) EndpointAddress,filterData 里填写的某服务的URL,比如:"http://<主机名>/vdir/s.svc/b"-->
        <!-- (4) EndpointAddressPrefix, 和(3)EndpointAddress类似,顾名思义地址前缀匹配,比如:""http://<主机名>/vdir/s.svc"-->
        <!-- (5) EndpointName, 比写全地址更灵活且不容易出错(service 配置节里的 EndpoingName) -->
        <!-- (6) XPath, 功能较上面更为强大,通过对 soap 消息的 XPath 定义匹配 -->
        <!-- (7) Custom, 自定义过滤器通过继承:CustomAssembly.MyCustomMsgFilter MessageFilter 重写 Match 方法实现 -->
        <!-- (8) And 不直接筛选消息中的值,而是通过它组合两个其他筛选器来创建 AND 条件 -->
        <!--     例如:<filter name="and1" filterType="And" filter1="address1" filter2="action1" /> -->
        <filter name="ServiceRouter_Filter1" filterType="MatchAll" />
        <!--
        <filter name="ServiceRouter_Filter2" filterType="Action" filterData="http://tempuri.org/IService1/GetName"/>
        <filter name="ServiceRouter_Filter3" filterType="Action" filterData="http://tempuri.org/IService1/SetName"/>
        -->
      </filters>
    </routing>

    <!-- client 配置节配置了路由服务要转发的真实服务地址 -->
    <client>
      <endpoint
             name="WcfService1" binding="wsHttpBinding"
             address="http://localhost:20000/WcfRoutingServiceTest/Service1/"
             contract="*">
      </endpoint>
    </client>
  </system.serviceModel>

<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
(3) 创建客户端应用
首先是客户端代理的生成,通过 Wcf Routing Service 好像没有办法能访问到真实服务的wsdl,
因此我的做法是先利用真实服务的wsdl生成代理,然后修改客户端配置文件
比如这个例子中真实服务地址是:http://localhost:20000/WcfRoutingServiceTest/Service1
Wcf Routing Service的地址是:    http://localhost:20002/WcfRoutingServiceTest/router/service1
修改后的客户端配置如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <client>
            <endpoint address="http://localhost:20002/WcfRoutingServiceTest/router/service1"
                binding="wsHttpBinding" contract="WcfService1.IService1" name="WcfService1" >
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>
客户端调用:
static void Main(string[] args)
{
    using (var client = new WcfService1.Service1Client())
    {
        var result = client.GetData(300);
        Console.WriteLine(result);
        result = client.GetData(200);
        Console.WriteLine(result);
        result = client.GetData(100);
        Console.WriteLine(result);

        client.SetName("test");
        Console.WriteLine(client.GetName());

        Console.Read();
    }
}
(4) 运行
WCF4.0 —— Routing Service_第2张图片
运行结果显示,client -> Routing Service -> Wcf Service1 注意当PerSession时,如果在RoutingService 每个filter 对于 WcfService1 都是独立的Session。
http://topic.csdn.net/u/20111005/15/69a4c0b8-63ab-4528-a520-6a8568ee17d3.html

----------------------------- 分割线 ----------------------------

2. IIS Hosting Request/Reply WCF Service Routing (示例代码下载)
当使用IIS Hosting时,我们创建的 WCF 工程模板就需要改为 Wcf Service Application了。下面介绍如何配置 Wcf Service Application 中的 Routing Service 
这次需要实现的是在一个 Routing Service 中路由多个不同地址的 Wcf Service (如文章开头的图片所示)
(1) Solution Overview
WCF4.0 —— Routing Service_第3张图片
其中:
WcfRoutingService : http://localhost:20001/RoutingService.svc
WcfService1 :          http://localhost:20002/Service1.svc
WcfService2:         http://localhost:20003/Service1.svc

(2) 配置 WcfRoutingService
因为 WcfRoutingService 里没有任何 svc 文件,这里利用了 WCF 4.0 的无 svc 文件激活服务的新特性:

<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
  <serviceActivations>
    <add relativeAddress="RoutingService.svc"
         service="System.ServiceModel.Routing.RoutingService,
                      System.ServiceModel.Routing, version=4.0.0.0, Culture=neutral,
                      PublicKeyToken=31bf3856ad364e35"  />
  </serviceActivations>
</serviceHostingEnvironment>
另外,上面已经提到对于 WCF Request/Reply 模型的 filterTable 不允许多播,那么在 Routing Service 里,需要配置响应个数的 Endpoint
完整配置如下:
<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  
  <system.serviceModel>   
    <bindings>
      <wsHttpBinding>
        <binding name="NonSecurityBinding">
          <security mode="None" />
        </binding>
      </wsHttpBinding>
    </bindings>
    
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true">
      <serviceActivations>
        <add relativeAddress="RoutingService.svc"
             service="System.ServiceModel.Routing.RoutingService,
                          System.ServiceModel.Routing, version=4.0.0.0, Culture=neutral,
                          PublicKeyToken=31bf3856ad364e35"  />
      </serviceActivations>
    </serviceHostingEnvironment>
    
    <services>
      <service name="System.ServiceModel.Routing.RoutingService" behaviorConfiguration="RoutingSvcBehaviorConfig">
        <endpoint address="service1" binding="basicHttpBinding"  name="service1Endpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter"/>
        <endpoint address="service2" binding="basicHttpBinding"  name="service2Endpoint" contract="System.ServiceModel.Routing.IRequestReplyRouter"/>
      </service>
    </services>
    
    <behaviors>
      <serviceBehaviors>
        <behavior name="RoutingSvcBehaviorConfig">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="True"/>
          <routing filterTableName="ServiceRoutingTable" soapProcessingEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    <routing>
      <filterTables>
        <!-- endprointName 对应上面 service/endpoint/name -->
        <filterTable name="ServiceRoutingTable">
          <add filterName="Service1Filter" endpointName="WcfService1" />
          <add filterName="Service2Filter" endpointName="WcfService2" />
        </filterTable>
      </filterTables>
      
      <filters>
        <filter name="Service1Filter" filterType="EndpointName" filterData="service1Endpoint" />
        <filter name="Service2Filter" filterType="EndpointName" filterData="service2Endpoint" />
      </filters>
    </routing>

    <client>
      <endpoint name="WcfService1" binding="basicHttpBinding" address="http://localhost:20002/Service1.svc" contract="*" />
      <endpoint name="WcfService2" binding="basicHttpBinding" address="http://localhost:20003/Service2.svc" contract="*" />
    </client>
   
  </system.serviceModel>
  
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
  
</configuration>
(3) 创建客户端应用
客户端配置文件
<?xml version="1.0"?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IService1" />
                <binding name="BasicHttpBinding_IService2" />
                <binding name="service1Endpoint" />
            </basicHttpBinding>
        </bindings>
      
        <client>
            <endpoint address="http://localhost:20001/RoutingService.svc/service1"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
                contract="WcfService1.IService1" name="service1Endpoint" />
            <endpoint address="http://localhost:20001/RoutingService.svc/service2"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService2"
                contract="WcfService2.IService2" name="service2Endpoint" />
        </client>
    </system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
调用
static void Main(string[] args)
 {
     // Test for WcfService1
     using (var service1client = new WcfService1.Service1Client("service1Endpoint"))
     {
         var result = service1client.GetData(300);
         Console.WriteLine(result);
     }

     // Test for WcfService2
     using (var service2client = new WcfService2.Service2Client("service2Endpoint"))
     {
         var result = service2client.GetData(100);
         Console.WriteLine(result);
     }

     Console.Read();
 }
(4) 运行
WCF4.0 —— Routing Service_第4张图片


----------------------------- 分割线 ----------------------------

参考:

http://msdn.microsoft.com/zh-cn/library/ee517419.aspx

http://blogs.profitbase.com/tsenn/?p=23

http://msdn.microsoft.com/zh-cn/library/ee517425.aspx

你可能感兴趣的:(filter,service,WCF,binding,behavior,compilation)