[WCF-Discovery] 实例演示:如何利用服务发现机制实现服务的“动态”调用?

前面两篇(《服务如何能被”发现”》和《客户端如何能够“探测”到可用的服务?》)我们分别介绍了可被发现服务如何被发布,以及客户端如果探测可用的服务。接下来我们通过一个简单的例子来演示如果创建和发布一个可被发现的服务,客户端如何在不知道服务终结点地址的情况下动态探测可用的服务并调用之。该实例的解决方案采用如右图所示的结构,即包含项目Service.Interface(类库)、Client(控制台应用)和Service(控制台应用)分别定义服务契约、服务(包括服务寄宿)和客户端程序。[源代码从这里下载,DynamicEndpoint方式进行服务调用源代码从这里下载]。

目录
步骤一、创建服务契约和服务
步骤二、寄宿服务
步骤三、服务的“动态”调用
DynamicEndpoint

步骤一、创建服务契约和服务

第一个步骤自然是在Service.Interface项目中定义代表服务契约的接口。我们还是采用属性的计算服务的例子,为此我们定义了如下一个ICalculator接口。

   1: using System.ServiceModel;
   2: namespace Artech.ServiceDiscovery.Service.Interface
   3: {
   4:     [ServiceContract(Namespace="http://www.artech.com/")]
   5:     public interface ICalculator
   6:     {
   7:         [OperationContract]
   8:         double Add(double x, double y);
   9:     }
  10: }

接下来在Service这个控制台应用项目中定义实现上述契约接口的服务CalculatorService,该服务类型定义如下。

   1: namespace Artech.ServiceDiscovery.Service
   2: {
   3:     public class CalculatorService : ICalculator
   4:     {
   5:         public double Add(double x, double y)
   6:         {
   7:             return x + y;
   8:         }
   9:     }
  10: }

步骤二、寄宿服务

接下来我们需要通过Service这个控制台应用作为宿主对上面定义的CalculatorService服务进行寄宿,下面是为此添加的配置。

   1: <configuration>
   2:     <system.serviceModel>
   3:         <behaviors>
   4:             <serviceBehaviors>
   5:                 <behavior>
   6:                     <serviceDiscovery />
   7:                 </behavior>
   8:             </serviceBehaviors>
   9:           <endpointBehaviors>
  10:             <behavior name="scopeMapping">
  11:               <endpointDiscovery enabled="true">
  12:                 <scopes>
  13:                   <add scope="http://www.artech.com/calculatorservice"/>
  14:                 </scopes>
  15:               </endpointDiscovery>
  16:             </behavior>
  17:           </endpointBehaviors>
  18:         </behaviors>
  19:         <services>
  20:             <service name="Artech.ServiceDiscovery.Service.CalculatorService">
  21:                 <endpoint address="http://127.0.0.1:3721/calculatorservice"  
  22:                           binding="ws2007HttpBinding" 
  23:                           contract="Artech.ServiceDiscovery.Service.Interface.ICalculator" 
  24:                           behaviorConfiguration="scopeMapping" />
  25:                 <endpoint kind="udpDiscoveryEndpoint" />
  26:             </service>
  27:         </services>
  28:     </system.serviceModel>
  29: </configuration>

在上面这段配置中,被寄宿的终结点出了有一个基于WS2007HttpBinding的终结点外,还具有另一个UdpDiscoveryEndpoint标准终结点。此外,我还定义了一个名称为scopeMapping的终结点行为,该行为通过EndpointDiscoveryBehavior行为定义了一个代表服务范围的Uri:http://www.artech.com/calculatorservice。这个终结点行为最终被应用到了第一个终结点),就以为这该终结点将此Uri作为了它的服务范围。最后,我还定义了一个默认的服务行为,而ServiceDiscoveryBehavior被定义其中。现在被寄宿的服务具有了ServiceDiscoveryBehavior行为和一个UdpDiscoveryEndpoint,所以它是一个可被发现的服务了。最后,该服务通过如下一段简单的程序进行自我寄宿。

   1: using System;
   2: using System.ServiceModel;
   3: namespace Artech.ServiceDiscovery.Service
   4: {
   5:     class Program
   6:     {
   7:         static void Main(string[] args)
   8:         {
   9:             using (ServiceHost host = new ServiceHost(typeof(CalculatorService)))
  10:             {
  11:                 host.Open();
  12:                 Console.Read();
  13:             }
  14:         }
  15:     }
  16: }

步骤三、服务的“动态”调用

现在来编写客户端服务调用的程序。假设客户端不知道服务的终结点地址,需要通过服务发现机制进行动态的探测。最终通过探测返回的终结点地址动态的创建服务代理对服务发起调用。我们不需要对客户端程序添加任何配置,可用服务的探测和调用完全通过如下的代码来实现。

   1: using System;
   2: using System.ServiceModel;
   3: using System.ServiceModel.Discovery;
   4: using Artech.ServiceDiscovery.Service.Interface;
   5: namespace Artech.ServiceDiscovery.Client
   6: {
   7:     class Program
   8:     {
   9:         static void Main(string[] args)
  10:         {
  11:             DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
  12:             FindCriteria criteria = new FindCriteria(typeof(ICalculator));
  13:             criteria.Scopes.Add(new Uri("http://www.artech.com/"));
  14:             FindResponse response = discoveryClient.Find(criteria);
  15:  
  16:             if (response.Endpoints.Count > 0)
  17:             {
  18:                 EndpointAddress address = response.Endpoints[0].Address;
  19:                 using(ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(new WS2007HttpBinding(),address))
  20:                 {
  21:                     ICalculator calculator = channelFactory.CreateChannel();
  22:                     Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, calculator.Add(1, 2));
  23:                 }
  24:             }
  25:             Console.Read();
  26:         }
  27:     }
  28: }

整段程序分为两个部分,即可用服务的探测和对目标服务的调用。首先我基于创建的标准终结点UdpDiscoveryEndpoint创建DiscoveryClient对象。然后基于服务契约接口的类型(ICalculator)创建FindCriteria,并在它的Scopes集合中添加了一个Uri(http://www.artech.com/")。由于我们不曾指定FindCriteria的MatchBy属性,默认采用基于前缀的服务范围匹配方式,所以通过这个Uri和我们的目标服务是可以匹配的。将此FindCriteria对象作为输入调用Find方法,并从返回的FindResponse中得到目标服务的终结点地址。最后用此终结点地址创建服务代理并进行服务调用。

整个实例程序编写完毕,再启动服务寄宿程序Service的前提下启动客户端程序Client,定义在Client中的服务调用能够顺利完成,并得到如下的输出结果。

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

DynamicEndpoint

在上面的例子中我们演示客户端在不知道目标服务地址的情况下如何服务发现机制进行服务的动态调用。从我们的演示来看,这需要两个基本的步骤:首先需要借助于DiscoveryClient通过服务探测(或者解析)获取进行服务调用必须的元数据(主要是目标服务终结点地址);然后根据获取的元数据信息创建服务代理进行服务调用。那么是否有一种方式能够将这两个步骤合二为一呢?答案是肯定的,这就涉及到对另一个标准终结点的使用,即DynamicEndpoint。

为了对DynamicEndpoint这个标准终结点的作用有一个感官的认识,我们借助于DynamicEndpoint对上面例子中的服务调用方式进行相应的更改。我们先为控制台应用Client添加一个配置文件,并定义如下一段简单的配置。

   1: <configuration>
   2:     <system.serviceModel>
   3:         <client>
   4:             <endpoint name="calculatorservice" 
   5:                       kind="dynamicEndpoint" 
   6:                       binding="ws2007HttpBinding" 
   7:                       contract="Artech.ServiceDiscovery.Service.Interface.ICalculator"/>
   8:         </client>     
   9:     </system.serviceModel>
  10: </configuration>

在这段配置中,我定义了一个客户端终结点。不过和我们之前的终结点配置有点不同,因为我们并没有对地址进行相应的设置。之所以可以省略掉对目标服务终结点地址的设置,在于我们定义的是一个DynamicEndpoint(kind="dynamicEndpoint")。而我们进行服务调用的程序和基于普通终结点的调用方式完全一样。运行修改后的程序,你会得到一样的执行结果。

   1: using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>("calculatorservice"))
   2: {
   3:     ICalculator calculator = channelFactory.CreateChannel();
   4:     Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, calculator.Add(1, 2));
   5: }

DynamicEndpoint之所以能够将服务探测和调用这两个步骤统一起来,其本质在于DynamicEndpoint是由两个终结点组合而成的。其中一个为用于进行服务探测的DiscoveryEndpoint;另一个用于真正服务调用的终结点,该终结点使用DynamicEndpoint的绑定和契约,而使用DiscoveryEndpoint探测的地址。关于DynamicEndpoint的组合性,也可以通过其定义看出来。

   1: public class DynamicEndpoint : ServiceEndpoint
   2: {
   3:     //其他成员
   4:     public DynamicEndpoint(ContractDescription contract, Binding binding);
   5:     public DiscoveryEndpointProvider DiscoveryEndpointProvider { get; set; }
   6:     public FindCriteria FindCriteria { get; set; }
   7: }

从DynamicEndpoint的定义可以看出:我们只需要通过指定终结点ABC三要素的绑定和契约就能够构建DynamicEndpoint这个标准终结点,而地址这是通过DiscoveryEndpoint终结点动态探测获得的。而具体负责创建这个DiscoveryEndpoint是通过属性DiscoveryEndpointProvider属性表示的DiscoveryEndpointProvider对象。至于FindCriteria属性,自然就是在进行服务探测指定匹配条件。

DiscoveryEndpointProvider是一个抽象类,DiscoveryEndpoint终结点的创建通过定义在该类上的唯一的抽象方法GetDiscoveryEndpoint实现。而WCF为了定义了两个具体的DiscoveryEndpointProvider,一个是UdpDiscoveryEndpointProvider,它会创建一个UdpDiscoveryEndpoint;另外一个为ConfigurationDiscoveryEndpointProvider,它会根据我们配置的来进行DiscoveryEndpoint的创建。下面的代码给出了DiscoveryEndpointProvider、UdpDiscoveryEndpointProvider和ConfigurationDiscoveryEndpointProvider的简单定义,从中可以看出后两个具体的DiscoveryEndpointProvider类型都是内部类型。

   1: public abstract class DiscoveryEndpointProvider
   2: {
   3:     public abstract DiscoveryEndpoint GetDiscoveryEndpoint();
   4: }
   5: internal class UdpDiscoveryEndpointProvider : DiscoveryEndpointProvider
   6: {
   7:     //省略成员
   8: }
   9: internal class ConfigurationDiscoveryEndpointProvider : DiscoveryEndpointProvider
  10: {
  11:     //省略成员
  12: }

在默认的情况下DynamicEndpoint采用的DiscoveryEndpointProvider是UdpDiscoveryEndpointProvider,也就是一位着DiscoveryEndpoint在进行真正的服务调用之前会先创建一个UdpDiscoveryEndpoint来探测可用调用的服务的终结点地址。从这个意义上讲,我们采用修改后采用DynamicEndpoint进行的服务调用,和之前先创建一个基于UdpDiscoveryEndpoint的DiscoveryClient对象探测出目标服务的终结点地址,在使用该地址创建服务代理进行服务调用的方式从本质上是一致的。

如果你不需要采用UdpDiscoveryEndpoint作为DynamicEndpoint默认使用的DiscoveryEndpoint,或者说你需要对被DynamicEndpoint使用的UdpDiscoveryEndpoint进行相应的设置,你都可以通过配置来完成。此外可供配置的还有表示服务探测匹配条件的FindCriteria。在下面的培植中,我针对DynamicEndpoint采用的UdpDiscoveryEndpoint进行了相应的设置,并为FindCriteria添加了一个表示服务反问的Uri。

   1: <configuration>
   2:     <system.serviceModel>
   3:         <client>
   4:             <endpoint name="calculatorservice"  
   5:                       kind="dynamicEndpoint" 
   6:                       endpointConfiguration="dynamicEndpointWithScope" 
   7:                       binding="ws2007HttpBinding" 
   8:                       contract="Artech.ServiceDiscovery.Service.Interface.ICalculator"/>
   9:         </client>
  10:       <standardEndpoints>
  11:         <dynamicEndpoint>
  12:           <standardEndpoint name="dynamicEndpointWithScope">
  13:             <discoveryClientSettings>
  14:               <endpoint kind="udpDiscoveryEndpoint" endpointConfiguration="adhocDiscoveryEndpointConfiguration"/>
  15:               <findCriteria>
  16:                 <scopes>
  17:                   <add scope="http://www.artech.com/"/>
  18:                 </scopes>
  19:               </findCriteria>
  20:             </discoveryClientSettings>
  21:           </standardEndpoint>
  22:         </dynamicEndpoint>
  23:         <udpDiscoveryEndpoint>
  24:           <standardEndpoint name="adhocDiscoveryEndpointConfiguration" discoveryVersion="WSDiscovery11">
  25:             <transportSettings duplicateMessageHistoryLength="2048"
  26:                                maxPendingMessageCount="5"
  27:                                maxReceivedMessageSize="8192"
  28:                                maxBufferPoolSize="262144"/>
  29:           </standardEndpoint>
  30:         </udpDiscoveryEndpoint>
  31:       </standardEndpoints>
  32:     </system.serviceModel>
  33: </configuration>

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-12-21 18:56:45

[WCF-Discovery] 实例演示:如何利用服务发现机制实现服务的“动态”调用?的相关文章

《Spring Cloud Netflix》-- 服务注册和服务发现-Eureka的服务认证和集群

一. Eureka的服务认证 1. 服务端添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 2. 服务端添加配置 服务认证 security.basic.enabled=true security.user.name=ronco

服务发现:Zookeeper vs etcd vs Consul

本文讲的是服务发现:Zookeeper vs etcd vs Consul,[编者的话]本文对比了Zookeeper.etcd和Consul三种服务发现工具,探讨了最佳的服务发现解决方案,仅供参考. 如果使用预定义的端口,服务越多,发生冲突的可能性越大,毕竟,不可能有两个服务监听同一个端口.管理一个拥挤的比方说被几百个服务所使用的所有端口的列表,本身就是一个挑战,添加到该列表后,这些服务需要的数据库和数量会日益增多.因此我们应该部署无需指定端口的服务,并且让Docker为我们分配一个随机的端口.

Docker生态系统系列之三:服务发现和分布式配置存储

本文讲的是Docker生态系统系列之三:服务发现和分布式配置存储,[编者的话]本文介绍了服务发现与全局可读配置存储两部分内容,不仅介绍了工作原理和工作方式,也介绍了与之相关的故障检测.重配置和安全问题,最后还介绍了常用的服务发现项目.整篇文章将这个知识点介绍的很全面细致,让读者能够对服务发现和全局可读配置存储有一个全面的认识,值得学习. 介绍 容器给寻找大规模设计与部署应用的需求提供了一个优雅的解决方案.在Docker提供实际容器技术的同时,许多其他的项目也在协助开发在部署环境中所需要的引导和沟

Docker网络和服务发现

本文讲的是Docker网络和服务发现[编者的话] 本文是<Docker网络和服务发现>一书的全文,作者是Michael Hausenblas.本文介绍了Docker世界中的网络和服务发现的工作原理,并提供了一系列解决方案. 前言 当你开始使用Docker构建应用的时候,对于Docker的能力和它带来的机会,你会感到很兴奋.它可以同时在开发环境和生产环境中运行,只需要将一切打包进一个Docker镜像中,然后通过Docker Hub分发镜像,这是很直接了当的.你会发现以下过程很令人满意:你可以快速

Spring Cloud 接入 EDAS 之服务发现篇

目前 EDAS 已经完全支持 Spring Cloud 应用的部署了,您可以直接将 Spring Cloud 应用部署到 EDAS 中. 同时,为了更好地将阿里中间件的功能以云服务的方式提供给大家,我们也对 Spring Cloud 中的一些组件进行了加强或替换的工作. 让我们先来聊聊服务发现.我们知道原生的 Spring Cloud 支持多种服务注册与发现的方式,Eureka . Consul . Zookeeper 等,目前使用最多最广的就是 Eureka了,那我们就先从一个简单的 Eure

10x系列之Clay.io的服务发现

本文讲的是10x系列之Clay.io的服务发现,[编者的话]Clay.io的Zoli Kahan撰写了"10X"系列博文,分享如何只使用一个很小的团队支撑Clay.io的大规模应用.本文是整个系列的第四篇,介绍如何构建一个服务发现系统. 架构 面向服务的架构是构建绝大多数产品的最可迭代和可用的软件配置之一.这些系统也遇到过很多问题,其中最大的问题可能就是服务发现问题.服务发现实际定义了你的服务如何与其它服务通信.Docker里也有这个问题.如果你不知道我们如何部署Docker,请参看D

[WCF安全系列]实例演示:TLS/SSL在WCF中的应用[SSL over TCP]

在接下来的系列文章中我们正是讨论关于身份认证的主题.在前面我们已经谈到了,WCF中的认证属于"双向认证",既包括服务对客户端的认证(以下简称客户端认证),也包括客户端对服务的认证(以下简称服务认证).客户端认证和服务认证从本质上并没有什么不同,无非都是被认证一方提供相应的用户凭证供对方对自己的身份进行验证.我们先来讨论服务认证,客户端认证放在后续的文章中. 在<从两种安全模式谈起>中,我们对TLS/SSL进行了简单的介绍.我们知道,客户端和服务在为建立安全上下文而进行的协商

利用阿里云容器服务实现Docker微服务间的负载均衡和服务发现

基于容器服务实现Docker微服务间的负载均衡和自动服务发现的方法 在容器服务上可以通过acsrouting将基于域名的http的服务暴漏出去,而且能够配合健康检查自动的负载均衡和服务发现,当其中一个容器出现问题之后,routing会自动将健康检查失败的容器从后端摘除,所以能做到自动的服务发现. 然而这个是将服务暴漏到外网的,那么服务间如何通过这种方式做到自动的服务发现和的负载均衡呢?容器服务引入了负载均衡的功能,只需要使用.local结尾的域名,并在依赖的服务的external_links中增

WCF服务编程设计规范(3):服务契约、数据契约和实例管理设计规范

WCF服务编程设计规范(3):服务契约.数据契约和实例管理设计规范.本节涵盖服务契约和数据契约设计规范,以及服务实例管理内容.中英对照版本,欢迎留言交流. Service Contracts 服务契约 1.Always apply the ServiceContract attribute on an interface, not a class: 把ServiceContract属性标记到契约接口上,而不是服务类上 //Avoid:避免 [ServiceContract] class MySe