閱讀648 返回首頁    go 阿裏雲 go 技術社區[雲棲]


[WCF 4.0新特性] 路由服務[實例篇]

在本篇文章中,我們將通過一個具體的實例來演示如何通過路由服務。在這個例子中,我們會創建連個簡單的服務HelloServie和GoodbyeService。假設客戶端不能直接調用這兩個服務,需要使用到路由服務作為兩者之間的中介。整個消息路由的場景如下圖所示,中間的GreetingService.svc就是代表路由服務,而兩個目標服務則通過HelloServie.svc和GoodbyeService.svc表示。路由服務使用的消息篩選器EndpointAddressMessageFilter,即根據包含在消息中的目標地址來決定應該將請求消息轉發給HelloServie.svc還是GoodbyeService.svc。[源代碼從這裏下載]

clip_image002

首先我們創建一個空的解決方案,並如下圖所示添加三個項目並添加相應的引用。其中類庫項目Service.Interface和Service分別用於定義服務契約和服務類型,而控製台項目Client用作為進行服務調用的客戶端。

clip_image003

在Service.Interface項目中為兩個服務創建服務契約接口:IHello和IGoodbye,兩個接口定義如下。

   1: using System.ServiceModel;
   2: namespace Artech.RoutingServiceDemo.Service.Interface
   3: {
   4:     [ServiceContract(Namespace="https://www.artech.com/")]
   5:     public interface IHello
   6:     {
   7:         [OperationContract]
   8:         string SayHello(string userName);
   9:     }
  10:     [ServiceContract(Namespace = "https://www.artech.com/")]
  11:     public interface IGoodbye
  12:     {
  13:         [OperationContract]
  14:         string SayGoodbye(string userName);
  15:     }
  16: }

然後在Service項目中定義實現兩個服務契約接口的服務類型:HelloService和GoodbyeService,具體定義如下。

   1: using Artech.RoutingServiceDemo.Service.Interface;
   2: namespace Service
   3: {
   4:     public class HelloService: IHello
   5:     {
   6:         public string SayHello(string userName)
   7:         {
   8:             return string.Format("Hello, {0}", userName);
   9:         }
  10:     }
  11:     public class GoodbyeService : IGoodbye
  12:     {
  13:         public string SayGoodbye(string userName)
  14:         {
  15:             return string.Format("Goodbye, {0}", userName);
  16:         }
  17:     }
  18: }

我們將上麵定義的兩個服務HelloService和GoodbyeService,以及路由服務RoutingService寄宿在IIS下。為此,我們直接在IIS管理器中創建一個Web應用(起名為“RoutingServiceDemo”),其物理地址之上Service項目的根目錄。然後,不要忘了將該項目的編譯後的輸出目錄從默認的\bin\Debug\改為。接下來在Service項目中添加一個Web.config, 並完成如下的配置。

   1: <configuration>
   2:     <system.serviceModel>
   3:         <behaviors>
   4:             <serviceBehaviors>
   5:                 <behavior name="routingBehavior">
   6:                     <routing filterTableName="greetingFilterTable" routeOnHeadersOnly="true" soapProcessingEnabled="true" />
   7:                 </behavior>
   8:             </serviceBehaviors>
   9:         </behaviors>
  10:         <services>
  11:             <service name="Service.HelloService">
  12:                 <endpoint binding="ws2007HttpBinding" contract="Artech.RoutingServiceDemo.Service.Interface.IHello" />
  13:             </service>
  14:             <service name="Service.GoodbyeService">
  15:                 <endpoint binding="ws2007HttpBinding" contract="Artech.RoutingServiceDemo.Service.Interface.IGoodbye" />
  16:             </service>
  17:             <service behaviorConfiguration="routingBehavior" name="System.ServiceModel.Routing.RoutingService">
  18:                 <endpoint binding="ws2007HttpBinding" contract="System.ServiceModel.Routing.IRequestReplyRouter" />
  19:             </service>
  20:         </services>
  21:       <client>
  22:         <endpoint name="helloService" address="https://127.0.0.1/RoutingServiceDemo/HelloService.svc" binding="ws2007HttpBinding" contract="*"/>
  23:         <endpoint name="goodbyeService" address="https://127.0.0.1/RoutingServiceDemo/GoodbyeService.svc" binding="ws2007HttpBinding" contract="*"/>
  24:       </client>
  25:       <routing>
  26:         <filters>
  27:           <filter name ="Address4HelloService" filterType ="EndpointAddress" filterData="https://127.0.0.1/RoutingServiceDemo/HelloService.svc"/>
  28:           <filter name ="Address4GoodbyeService" filterType ="EndpointAddress" filterData="https://127.0.0.1/RoutingServiceDemo/GoodbyeService.svc"/>
  29:         </filters>
  30:         <filterTables>
  31:           <filterTable name="greetingFilterTable">
  32:             <add filterName="Address4HelloService" endpointName="helloService"/>
  33:             <add filterName="Address4GoodbyeService" endpointName="goodbyeService"/>            
  34:           </filterTable>
  35:         </filterTables>
  36:       </routing> 
  37:       <serviceHostingEnvironment>
  38:         <serviceActivations>
  39:           <add relativeAddress="HelloService.svc" service="Service.HelloService"/>
  40:           <add relativeAddress="GoodbyeService.svc" service="Service.GoodbyeService"/>
  41:           <add relativeAddress="GrettingService.svc" service="System.ServiceModel.Routing.RoutingService, 
  42: System.ServiceModel.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
  43:         </serviceActivations>
  44:       </serviceHostingEnvironment>
  45:     </system.serviceModel>
  46: </configuration>

我們對上述的這段配置進行一下簡單的分析。首先,我們按照“”方式(《標準終結點與無(.SVC)文件服務激活》)對服務HelloService、GoodbyeService和路由服務RoutingService進行寄宿,它們的相對地址分別為HelloService.svc、GoodbyeService.svc和GreetingService.svc。它們都具有一個唯一的基於WS2007HttpBinding的終結點。由於我們需要路由服務采用請求/回複模式進行消息路由,我們將契約指定為。

   1: <configuration>
   2:     <system.serviceModel>        
   3:         <services>
   4:             <service name="Service.HelloService">
   5:                 <endpoint binding="ws2007HttpBinding" contract="Artech.RoutingServiceDemo.Service.Interface.IHello" />
   6:             </service>
   7:             <service name="Service.GoodbyeService">
   8:                 <endpoint binding="ws2007HttpBinding" contract="Artech.RoutingServiceDemo.Service.Interface.IGoodbye" />
   9:             </service>
  10:             <service behaviorConfiguration="routingBehavior" name="System.ServiceModel.Routing.RoutingService">
  11:                 <endpoint binding="ws2007HttpBinding" contract="System.ServiceModel.Routing.IRequestReplyRouter" />
  12:             </service>
  13:         </services>      
  14:       <serviceHostingEnvironment>
  15:         <serviceActivations>
  16:           <add relativeAddress="HelloService.svc" service="Service.HelloService"/>
  17:           <add relativeAddress="GoodbyeService.svc" service="Service.GoodbyeService"/>
  18:           <add relativeAddress="GrettingService.svc" service="System.ServiceModel.Routing.RoutingService, 
  19: System.ServiceModel.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
  20:         </serviceActivations>
  21:       </serviceHostingEnvironment>
  22:       ...
  23:     </system.serviceModel>
  24: </configuration>

路由服務上應用了服務行為RoutingBehavior,配置在該行為上名稱為greetingFilterTable的篩選器表定義如下。該篩選器表它具有兩個基於EndpointAddressMessageFilter的消息篩選器,配置名稱分別為“Address4HelloService”和“Address4GoodbyeService”,它們分別將目標服務HelloService和GoodbyeService的地址作為篩選條件。而這兩個篩選器對應著指向目標服務的客戶端終結點“helloService”和“goodbyeService”。

   1: <configuration>   
   2: <system.serviceModel>
   3:        <client>
   4:         <endpoint name="helloService" address="https://127.0.0.1/RoutingServiceDemo/HelloService.svc" binding="ws2007HttpBinding" contract="*"/>
   5:         <endpoint name="goodbyeService" address="https://127.0.0.1/RoutingServiceDemo/GoodbyeService.svc" binding="ws2007HttpBinding" contract="*"/>
   6:       </client>
   7:       <routing>
   8:         <filters>
   9:           <filter name ="Address4HelloService" filterType ="EndpointAddress" filterData="https://127.0.0.1/RoutingServiceDemo/HelloService.svc"/>
  10:           <filter name ="Address4GoodbyeService" filterType ="EndpointAddress" filterData="https://127.0.0.1/RoutingServiceDemo/GoodbyeService.svc"/>
  11:         </filters>
  12:         <filterTables>
  13:           <filterTable name="greetingFilterTable">
  14:             <add filterName="Address4HelloService" endpointName="helloService"/>
  15:             <add filterName="Address4GoodbyeService" endpointName="goodbyeService"/>            
  16:           </filterTable>
  17:         </filterTables>
  18:       </routing> 
  19:     </system.serviceModel>
  20: </configuration>

對於上述的配置,細心的讀者也許發現一個特殊之處:定義在<client>配置節下的被路由服務使用的終結點的契約被設置成“”(contract=”*”)。照理說,這裏的契約應該設置成路由服務實現的服務契約System.ServiceModel.Routing.IRequestReplyRouter才對。但是你真的進行了如此的設置,因為將路由服務使用的客戶端終結點契約設置成“*”是個強製性的規定。

由於調用服務的消息需要通過路由服務這個中介才能抵達真正的目標服務,所以客戶端我們需要將路由服務的地址作為消息發送的目標地址。在這裏,我們通過這個終結點行為實現了物理地址(消息真正發送的目標地址)和邏輯地址(終結點地址)的分離,將消息發送給路由服務的地址:https://127.0.0.1/RoutingServiceDemo/GrettingService.svc。下麵的XML片斷代表整個客戶端的配置,而ClientViaBehavior被定義成默認的終結點行為。

   1: <configuration>
   2:   <system.serviceModel>
   3:     <behaviors>
   4:       <endpointBehaviors>
   5:         <behavior>
   6:           <clientVia viaUri="https://127.0.0.1/RoutingServiceDemo/GrettingService.svc"/>
   7:         </behavior>
   8:       </endpointBehaviors>
   9:     </behaviors>
  10:     <client>
  11:       <endpoint name="helloService" 
  12:                 address="https://127.0.0.1/RoutingServiceDemo/HelloService.svc" 
  13:                 binding="ws2007HttpBinding" 
  14:                 contract="Artech.RoutingServiceDemo.Service.Interface.IHello"/>
  15:       <endpoint name="goodbyeService" 
  16:                 address="https://127.0.0.1/RoutingServiceDemo/GoodbyeService.svc" 
  17:                 binding="ws2007HttpBinding" 
  18:                 contract="Artech.RoutingServiceDemo.Service.Interface.IGoodbye"/>      
  19:     </client>
  20:   </system.serviceModel>
  21: </configuration>

借助於這樣的配置,你可以按照傳統的編程方式進行服務的調用,無需再考慮底層消息路由機製的存在。

   1: using (ChannelFactory<IHello> channelFactoryHello = new ChannelFactory<IHello>("helloService"))
   2: using (ChannelFactory<IGoodbye> channelFactoryGoodbye = new ChannelFactory<IGoodbye>("goodbyeService"))
   3: {
   4:     IHello helloProxy = channelFactoryHello.CreateChannel();
   5:     IGoodbye goodbyeProxy = channelFactoryGoodbye.CreateChannel();
   6:     Console.WriteLine(helloProxy.SayHello("Zhang San"));
   7:     Console.WriteLine(goodbyeProxy.SayGoodbye("Li Si"));
   8: }

輸出結果:

   1: Hello, Zhang San
   2: Goodbye, Li Si

 

[WCF 4.0新特性] 路由服務[原理篇]
[WCF 4.0新特性] 路由服務[實例篇]


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-26 15:33:55

  上一篇:go  [WCF 4.0新特性] 路由服務[原理篇]
  下一篇:go  如何在EHAB(EntLib)中定義”細粒度”異常策略?