[WCF-Discovery] 客户端如何能够“探测”到可用的服务?
当应用了ServiceDiscoveryBehavior行为的服务通过标准终结点DiscoveryEndpoint被发布出来之后(《[WCF-Discovery]服务如何能被”发现”》),客户端就可以按照WS-Discovery中定义的方式对可用的目标方式进行探测和解析了。由于这个过程本质上就是一次普通的服务调用,具体来说是针对发布发现服务(非目标服务)的标准终结点DiscoveryEndpoint的调用,所以客户端也需要具有这么一个匹配的终结点。
目录:
一、DiscoveryClient
二、FindCriteria/FindResponse
三、ResolveCriteria/ResolveResponse
一、DiscoveryClient
客户端针对可用目标服务的探测与解析都是通过DiscoveryClient对象来实现的,下面的代码片断给出了DiscoveryClient的定义。我们可以直接通过一个DiscoveryEndpoint对象,或者是DiscoveryEndpoint的配置名称来创建DiscoveryClient对象。
public sealed class DiscoveryClient : ICommunicationObject, IDisposable,...
{
//事件
public event EventHandler<FindCompletedEventArgs> FindCompleted;
public event EventHandler<ResolveCompletedEventArgs> ResolveCompleted;
//构造函数
public DiscoveryClient(DiscoveryEndpoint discoveryEndpoint);
public DiscoveryClient(string endpointConfigurationName);
//Find
public FindResponse Find(FindCriteria criteria);
public void FindAsync(FindCriteria criteria);
public void FindAsync(FindCriteria criteria, object userState);
//Resolve
public ResolveResponse Resolve(ResolveCriteria criteria);
public void ResolveAsync(ResolveCriteria criteria);
public void ResolveAsync(ResolveCriteria criteria, object userState);
}
不论是用于可用服务探测的Find/FindAsync,还是用于目标服务解析的Resolve/ResolveAysnc,都需要指定相应的匹配条件。前者对应的匹配条件通过类型FindCriteria来表示,而后者匹配条件的类型则是ResolveCriteria。
同步方法Find/Resolve的返回类型分别为FindResponse和ResolveResponse。而对于异步调用,则可以通过注册的FindCompleted/ResolveCompleted事件参数中获取类型为FindResponse/ResolveResponse的返回值。这两个事件的参数类型分别为FindCompletedEventArgs和ResolveCompletedEventArgs,这个两个类型和它们的基类System.ComponentModel.AsyncCompletedEventArgs定义如下。
public class FindCompletedEventArgs : AsyncCompletedEventArgs
{
//其他成员
public FindResponse Result { get; }
}
public class ResolveCompletedEventArgs : AsyncCompletedEventArgs
{
//其他成员
public ResolveResponse Result { get; }
}
public class AsyncCompletedEventArgs : EventArgs
{
//其他成员
public bool Cancelled { get; }
public Exception Error { get; }
public object UserState { get; }
}
二、FindCriteria/ FindResponse
代表Probe请求的Find方法接受一个FindCriteria类的输入参数作为进行探测可用目标的匹配条件,该类型的主要的属性成员定义如下。其中ContractTypeNames代表探测的目标服务实现的契约类型列表,而Scopes和ScopeMatchBy则分别代表了用于探测目标的的范围和对范围进行匹配的方式。
public class FindCriteria
{
//其他成员
public static readonly Uri ScopeMatchByExact;
public static readonly Uri ScopeMatchByLdap;
public static readonly Uri ScopeMatchByNone;
public static readonly Uri ScopeMatchByPrefix;
public static readonly Uri ScopeMatchByUuid;
public Collection<XmlQualifiedName> ContractTypeNames { get; }
public Collection<Uri> Scopes { get; }
public Uri ScopeMatchBy { get; set; }
}
public class EndpointDiscoveryBehavior : IEndpointBehavior
{
//其他成员
public Collection<Uri> Scopes { get; }
}
<configuration>
<system.serviceModel>
<services>
<service ...>
<endpoint behaviorConfiguration="scopeMatch" .../>
...
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="scopeMatch">
<endpointDiscovery>
<scopes>
<add scope="http://www.example.com/calculator"/>
<add scope="ldap:///ou=engineering,o=examplecom,c=us"/>
</scopes>
</endpointDiscovery>
</behavior>
</endpointBehaviors>
</system.serviceModel>
</configuration>
FindCriteria的ScopeMatchBy属性类型依然是Uri。WCF预选定义了5个Uri代表相应的进行范围匹配的5种算法,它们对应着定义在FindCriteria的5个静态只读属性:ScopeMatchByExact、ScopeMatchByLdap、ScopeMatchByNone、ScopeMatchByPrefix和ScopeMatchByUuid。
public class FindCriteria
{
//其他成员
public static readonly Uri ScopeMatchByExact;
public static readonly Uri ScopeMatchByLdap;
public static readonly Uri ScopeMatchByNone;
public static readonly Uri ScopeMatchByPrefix;
public static readonly Uri ScopeMatchByUuid;
public Uri ScopeMatchBy { get; set; }
}
- ScopeMatchByExact:对Uri进行精确匹配,Uri为http://schemas.microsoft.com/ws/2008/06/discovery/strcmp0;
- ScopeMatchByPrefix:将指定的Uri作为服务范围的前缀进行匹配,Uri为http://schemas.microsoft.com/ws/2008/06/discovery/rfc;
- ScopeMatchByLdap:按使用LDAP URL的段来匹配范围,Uri为http://schemas.microsoft.com/ws/2008/06/discovery/ldap;
- ScopeMatchByUuid:通过使用UUID字符串来完全匹配范围,Uri为http://schemas.microsoft.com/ws/2008/06/discovery/uuid;
- ScopeMatchByNone:仅匹配那些未指定范围的服务,Uri为http://schemas.microsoft.com/ws/2008/06/discovery/none。
如果采用ScopeMatchByExact,进行精确匹配是区分大小写的。而基于前缀匹配的ScopeMatchByPrefix,实际上按以“/”分隔的段进行匹配。搜索 http://contoso/building1 与范围为 http://contoso/building/floor1 的服务相匹配。请注意,该搜索与http://contoso/building100 不匹配,因为最后两个段不匹配。ScopeMatchBy的值必须指定为上述的5种Uri之一,其他各式的Uri是无效的。如果未指定范围匹配规则,则使用ScopeMatchByPrefix。
按照WS-Discovery定义的消息交换模式来看,客户端针对Find/FindAsync方法调用实际上就是发送Probe请求。符合匹配条件的目标服务会回复以PM消息,该消息中会包含服务相关的元数据信息。最终这些PM消息中的内容会被提取出来,被封装成FindResponse对象并最为Find方法的返回值(或者事件参数FindCompletedEventArgs的Result属性)。接下来,我们将重点介绍FindResponse这个类型。
public class FindResponse
{
//其他成员
public DiscoveryMessageSequence GetMessageSequence(EndpointDiscoveryMetadata endpointDiscoveryMetadata);
public Collection<EndpointDiscoveryMetadata> Endpoints { get; }
}
public class EndpointDiscoveryMetadata
{
//其他成员
public EndpointAddress Address { get; set; }
public Collection<XmlQualifiedName> ContractTypeNames { get; }
public Collection<XElement> Extensions { get; }
public Collection<Uri> ListenUris { get; }
public Collection<Uri> Scopes { get; }
public int Version { get; set; }
}
public class DiscoveryMessageSequence : ...
{
//其他成员
public long InstanceId {get; }
public long MessageNumber {get; }
public Uri SequenceId {get; }
}
上面我们介绍了用于进行可用服务探测的Find/FindAsync操作的输入和输出,接下俩我们按照相同的方式来分析用于进行服务解析的Resolve/ResolveAsync操作的输入和输出。首先来介绍一下用于封装匹配条件的ResolveCriteria类型,下面给出了它核心的属性定义。
public class ResolveCriteria
{
//其他成员
public EndpointAddress Address { get; set; }
public Collection<XElement> Extensions { get; }
public TimeSpan Duration { get; set; }
}
而作为Resolve/ResolveAsync输出的ResolveResponse类型定义很简单。它具有两个核心的只读属性,代表被解析后的服务终结点元数据的EndpointDiscoveryMetadata属性和代表消息序列的MessageSequence属性。
public class ResolveResponse
{
//其他成员
public EndpointDiscoveryMetadata EndpointDiscoveryMetadata { get;}
public DiscoveryMessageSequence MessageSequence { get;}
}