[WCF-Discovery] 實例演示:如何利用服務發現機制實現服務的“動態”調用?
前面兩篇(《服務如何能被”發現”》和《客戶端如何能夠“探測”到可用的服務?》)我們分別介紹了可被發現服務如何被發布,以及客戶端如果探測可用的服務。接下來我們通過一個簡單的例子來演示如果創建和發布一個可被發現的服務,客戶端如何在不知道服務終結點地址的情況下動態探測可用的服務并調用之。該實例的解決方案采用如下圖所示的結構,即包含項目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); } }
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>
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(); } } }
整個實例程序編寫完畢,再啟動服務寄宿程序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>
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)); }
public class DynamicEndpoint : ServiceEndpoint { //其他成員 public DynamicEndpoint(ContractDescription contract, Binding binding); public DiscoveryEndpointProvider DiscoveryEndpointProvider { get; set; } public FindCriteria FindCriteria { get; set; } }
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 { //省略成員 }
如果你不需要采用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>