现在的位置: 首页 > 综合 > 正文

WCF4.0 —— Routing Service

2013年08月15日 ⁄ 综合 ⁄ 共 10548字 ⁄ 字号 评论关闭

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

路由服务使用的消息筛选器提供常用消息选择功能,例如,终结点的名称、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) 运行

运行结果显示,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

其中:
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) 运行

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

参考:

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

抱歉!评论已关闭.