[WCF-Discovery] 實例演示:如何利用服務發現機制實現服務的“動態”調用?

作者: Artech  來源: 博客園  發布時間: 2011-12-22 22:58  閱讀: 3456 次  推薦: 0   原文鏈接   [收藏]  

  前面兩篇(《服務如何能被”發現”》和《客戶端如何能夠“探測”到可用的服務?》)我們分別介紹了可被發現服務如何被發布,以及客戶端如果探測可用的服務。接下來我們通過一個簡單的例子來演示如果創建和發布一個可被發現的服務,客戶端如何在不知道服務終結點地址的情況下動態探測可用的服務并調用之。該實例的解決方案采用如下圖所示的結構,即包含項目Service.Interface(類庫)、Client(控制臺應用)和Service(控制臺應用)分別定義服務契約、服務(包括服務寄宿)和客戶端程序。[源代碼從這里下載,DynamicEndpoint方式進行服務調用源代碼從這里下載]。

目錄
步驟一、創建服務契約和服務
步驟二、寄宿服務
步驟三、服務的“動態”調用
DynamicEndpoint

  步驟一、創建服務契約和服務

  第一個步驟自然是在Service.Interface項目中定義代表服務契約的接口。我們還是采用屬性的計算服務的例子,為此我們定義了如下一個ICalculator接口。

using System.ServiceModel;
namespace Artech.ServiceDiscovery.Service.Interface
{
    [ServiceContract(Namespace="http://www.artech.com/")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double x, double y);
    }
}
  接下來在Service這個控制臺應用項目中定義實現上述契約接口的服務CalculatorService,該服務類型定義如下。
namespace Artech.ServiceDiscovery.Service
{
    public class CalculatorService : ICalculator
    {
        public double Add(double x, double y)
        {
            return x + y;
        }
    }
}
  步驟二、寄宿服務

  接下來我們需要通過Service這個控制臺應用作為宿主對上面定義的CalculatorService服務進行寄宿,下面是為此添加的配置。

<configuration>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior>
                    <serviceDiscovery />
                </behavior>
            </serviceBehaviors>
          <endpointBehaviors>
            <behavior name="scopeMapping">
              <endpointDiscovery enabled="true">
                <scopes>
                  <add scope="http://www.artech.com/calculatorservice"/>
                </scopes>
              </endpointDiscovery>
            </behavior>
          </endpointBehaviors>
        </behaviors>
        <services>
           <service name="Artech.ServiceDiscovery.Service.CalculatorService">
                <endpoint address="http://127.0.0.1:3721/calculatorservice"  
                          binding="ws2007HttpBinding" 
                          contract="Artech.ServiceDiscovery.Service.Interface.ICalculator" 
                          behaviorConfiguration="scopeMapping"/>
                <endpoint kind="udpDiscoveryEndpoint"/>
            </service>
        </services>
    </system.serviceModel>
</configuration>
  在上面這段配置中,被寄宿的終結點出了有一個基于WS2007HttpBinding的終結點外,還具有另一個UdpDiscoveryEndpoint標準終結點。此外,我還定義了一個名稱為scopeMapping的終結點行為,該行為通過EndpointDiscoveryBehavior行為定義了一個代表服務范圍的Uri:http://www.artech.com/calculatorservice。這個終結點行為最終被應用到了第一個終結點),就以為這該終結點將此Uri作為了它的服務范圍。最后,我還定義了一個默認的服務行為,而ServiceDiscoveryBehavior被定義其中。現在被寄宿的服務具有了ServiceDiscoveryBehavior行為和一個UdpDiscoveryEndpoint,所以它是一個可被發現的服務了。最后,該服務通過如下一段簡單的程序進行自我寄宿。
using System;
using System.ServiceModel;
namespace Artech.ServiceDiscovery.Service
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
            {
                host.Open();
                Console.Read();
            }
        }
    }
}
  步驟三、服務的“動態”調用

  現在來編寫客戶端服務調用的程序。假設客戶端不知道服務的終結點地址,需要通過服務發現機制進行動態的探測。最終通過探測返回的終結點地址動態的創建服務代理對服務發起調用。我們不需要對客戶端程序添加任何配置,可用服務的探測和調用完全通過如下的代碼來實現。

using System;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using Artech.ServiceDiscovery.Service.Interface;
namespace Artech.ServiceDiscovery.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
            FindCriteria criteria = new FindCriteria(typeof(ICalculator));
            criteria.Scopes.Add(new Uri("http://www.artech.com/"));
            FindResponse response = discoveryClient.Find(criteria);
 
            if (response.Endpoints.Count > 0)
            {
                EndpointAddress address = response.Endpoints[0].Address;
                using(ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(new WS2007HttpBinding(),address))
                {
                    ICalculator calculator = channelFactory.CreateChannel();
                    Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, calculator.Add(1, 2));
                }
            }
            Console.Read();
        }
    }
}
  整段程序分為兩個部分,即可用服務的探測和對目標服務的調用。首先我基于創建的標準終結點UdpDiscoveryEndpoint創建DiscoveryClient對象。然后基于服務契約接口的類型(ICalculator)創建FindCriteria,并在它的Scopes集合中添加了一個Uri(http://www.artech.com/")。由于我們不曾指定FindCriteria的MatchBy屬性,默認采用基于前綴的服務范圍匹配方式,所以通過這個Uri和我們的目標服務是可以匹配的。將此FindCriteria對象作為輸入調用Find方法,并從返回的FindResponse中得到目標服務的終結點地址。最后用此終結點地址創建服務代理并進行服務調用。

  整個實例程序編寫完畢,再啟動服務寄宿程序Service的前提下啟動客戶端程序Client,定義在Client中的服務調用能夠順利完成,并得到如下的輸出結果。

x + y = 3 when x = 1 and y = 2

  DynamicEndpoint

  在上面的例子中我們演示客戶端在不知道目標服務地址的情況下如何服務發現機制進行服務的動態調用。從我們的演示來看,這需要兩個基本的步驟:首先需要借助于DiscoveryClient通過服務探測(或者解析)獲取進行服務調用必須的元數據(主要是目標服務終結點地址);然后根據獲取的元數據信息創建服務代理進行服務調用。那么是否有一種方式能夠將這兩個步驟合二為一呢?答案是肯定的,這就涉及到對另一個標準終結點的使用,即DynamicEndpoint。

  為了對DynamicEndpoint這個標準終結點的作用有一個感官的認識,我們借助于DynamicEndpoint對上面例子中的服務調用方式進行相應的更改。我們先為控制臺應用Client添加一個配置文件,并定義如下一段簡單的配置。

<configuration>
    <system.serviceModel>
        <client>
            <endpoint name="calculatorservice" 
                      kind="dynamicEndpoint" 
                      binding="ws2007HttpBinding" 
                      contract="Artech.ServiceDiscovery.Service.Interface.ICalculator"/>
        </client>     
    </system.serviceModel>
</configuration>
  在這段配置中,我定義了一個客戶端終結點。不過和我們之前的終結點配置有點不同,因為我們并沒有對地址進行相應的設置。之所以可以省略掉對目標服務終結點地址的設置,在于我們定義的是一個DynamicEndpoint(kind="dynamicEndpoint")。而我們進行服務調用的程序和基于普通終結點的調用方式完全一樣。運行修改后的程序,你會得到一樣的執行結果。
using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
{
    ICalculator calculator = channelFactory.CreateChannel();
    Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, calculator.Add(1, 2));
}
  DynamicEndpoint之所以能夠將服務探測和調用這兩個步驟統一起來,其本質在于DynamicEndpoint是由兩個終結點組合而成的。其中一個為用于進行服務探測的DiscoveryEndpoint;另一個用于真正服務調用的終結點,該終結點使用DynamicEndpoint的綁定和契約,而使用DiscoveryEndpoint探測的地址。關于DynamicEndpoint的組合性,也可以通過其定義看出來。
public class DynamicEndpoint : ServiceEndpoint
{
    //其他成員
    public DynamicEndpoint(ContractDescription contract, Binding binding);
    public DiscoveryEndpointProvider DiscoveryEndpointProvider { get; set; }
    public FindCriteria FindCriteria { get; set; }
}
  從DynamicEndpoint的定義可以看出:我們只需要通過指定終結點ABC三要素的綁定和契約就能夠構建DynamicEndpoint這個標準終結點,而地址這是通過DiscoveryEndpoint終結點動態探測獲得的。而具體負責創建這個DiscoveryEndpoint是通過屬性DiscoveryEndpointProvider屬性表示的DiscoveryEndpointProvider對象。至于FindCriteria屬性,自然就是在進行服務探測指定匹配條件。

  DiscoveryEndpointProvider是一個抽象類,DiscoveryEndpoint終結點的創建通過定義在該類上的唯一的抽象方法GetDiscoveryEndpoint實現。而WCF為了定義了兩個具體的DiscoveryEndpointProvider,一個是UdpDiscoveryEndpointProvider,它會創建一個UdpDiscoveryEndpoint;另外一個為ConfigurationDiscoveryEndpointProvider,它會根據我們配置的來進行DiscoveryEndpoint的創建。下面的代碼給出了DiscoveryEndpointProvider、UdpDiscoveryEndpointProvider和ConfigurationDiscoveryEndpointProvider的簡單定義,從中可以看出后兩個具體的DiscoveryEndpointProvider類型都是內部類型。

public abstract class DiscoveryEndpointProvider
{
    public abstract DiscoveryEndpoint GetDiscoveryEndpoint();
}
internal class UdpDiscoveryEndpointProvider : DiscoveryEndpointProvider
{
    //省略成員
}
internal class ConfigurationDiscoveryEndpointProvider : DiscoveryEndpointProvider
{
    //省略成員
}
  在默認的情況下DynamicEndpoint采用的DiscoveryEndpointProvider是UdpDiscoveryEndpointProvider,也就是一位著DiscoveryEndpoint在進行真正的服務調用之前會先創建一個UdpDiscoveryEndpoint來探測可用調用的服務的終結點地址。從這個意義上講,我們采用修改后采用DynamicEndpoint進行的服務調用,和之前先創建一個基于UdpDiscoveryEndpoint的DiscoveryClient對象探測出目標服務的終結點地址,在使用該地址創建服務代理進行服務調用的方式從本質上是一致的。

  如果你不需要采用UdpDiscoveryEndpoint作為DynamicEndpoint默認使用的DiscoveryEndpoint, 或者說你需要對被DynamicEndpoint使用的UdpDiscoveryEndpoint進行相應的設置,你都可以通過配置來完成。此外可供配置的還有表示服務探測匹配條件的FindCriteria。在下面的培植中,我針對DynamicEndpoint采用的UdpDiscoveryEndpoint進行了相應的設置,并為FindCriteria添加了一個表示服務反問的Uri。

<configuration>
    <system.serviceModel>
        <client>
            <endpoint name="calculatorservice"  
                      kind="dynamicEndpoint" 
                      endpointConfiguration="dynamicEndpointWithScope" 
                      binding="ws2007HttpBinding" 
                      contract="Artech.ServiceDiscovery.Service.Interface.ICalculator"/>
        </client>
      <standardEndpoints>
        <dynamicEndpoint>
          <standardEndpoint name="dynamicEndpointWithScope">
            <discoveryClientSettings>
              <endpoint kind="udpDiscoveryEndpoint" endpointConfiguration="adhocDiscoveryEndpointConfiguration"/>
              <findCriteria>
                <scopes>
                  <add scope="http://www.artech.com/"/>
                </scopes>
              </findCriteria>
            </discoveryClientSettings>
          </standardEndpoint>
        </dynamicEndpoint>
        <udpDiscoveryEndpoint>
          <standardEndpoint name="adhocDiscoveryEndpointConfiguration" discoveryVersion="WSDiscovery11">
            <transportSettings duplicateMessageHistoryLength="2048"
                               maxPendingMessageCount="5"
                               maxReceivedMessageSize="8192"
                               maxBufferPoolSize="262144"/>
          </standardEndpoint>
        </udpDiscoveryEndpoint>
      </standardEndpoints>
    </system.serviceModel>
</configuration>
0
0
 
標簽:WCF
 
 

文章列表

arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()