[WCF-Discovery] 客戶端如何能夠“探測”到可用的服務?

作者: Artech  來源: 博客園  發布時間: 2011-10-09 17:49  閱讀: 640 次  推薦: 0   原文鏈接   [收藏]  

  當應用了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);
}
  DiscoveryClient定義了兩套方法,一套是Find/FindAsync,另一套則是Resolve/ResolveAysnc。實際上Find相當于是WS-Discovery中的Probe,而Resolve自然就是WS-Discovery中的Resolve了。其中Find/Resolve采用同步的調用方式,而FindAsync/ ResolveAysnc則采用異步調用,異步調用完成的時候會觸發事件FindCompleted/ResolveCompleted。

  不論是用于可用服務探測的Find/FindAsync,還是用于目標服務解析的Resolve/ResolveAysnc,都需要指定相應的匹配條件。前者對應的匹配條件通過類型FindCriteria來表示,而后者匹配條件的類型則是ResolveCriteria

  同步方法Find/Resolve的返回類型分別為FindResponseResolveResponse。而對于異步調用,則可以通過注冊的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; }
}
  目標的探測范圍通過一個Uri的集合表示。客戶端要通過范圍進行目標服務的探測,前提是目標服務預先得與表示范圍的Uri相關聯。服務(實際上是指服務的某個終結點)的范圍關聯通過終結點行為EndpointDiscoveryBehavior來指定。如下面的代碼片斷所示,和FindCriteria一樣,EndpointDiscoveryBehavior同樣具有一個Uri集合類型的Scopes屬性。
public class EndpointDiscoveryBehavior : IEndpointBehavior
{
    //其他成員
   public Collection<Uri> Scopes { get; }
}
  在服務寄宿的時候,我們將表示服務范圍的Uri列表定義在EndpointDiscoveryBehavior終結點行為中,并通過將此行為應用在寄宿服務相應的終結點上,從而實現了服務(終結點)與范圍的關聯。在下面的配置中,我定義了一個名為scopeMatch的終結點行為將表示服務范圍的兩個Uri應用到了服務的終結點上。
<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屬性。

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; }
}
  下面的列表列出了這5個靜態字段分別代表了何種服務范圍匹配算法,以及各自具有怎樣的Uri值。實際上這些代表服務范圍匹配算法的Uri也是定義在WS-Discovery規范之中,但是為了避免為實現對不同版本的WS-Discovery的支持而采用不同的Uri,WCF在這里并沒有真正地采用定義在相應版本的WS-Discovery中的Uri,而是定義了自己的常量。在對Probe消息進行序列化的時候,會轉換成相應WS-Discovery支持的Uri。

  如果采用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; }
}
  如上面的代碼所示,FindResponse具有一個核心的只讀屬性Endpoints,其類型是一個元素類型為EndpointDiscoveryMetadata的集合。顧名思義,EndpointDiscoveryMetadata就是代表探測到的服務制定匹配條件的服務的終結點的元數據。EndpointDiscoveryMetadata定義如下,通過相應的屬性可以得到代表目標服務終結點的地址、契約類型列表、監聽地址、服務范圍和擴展和版本相關信息。
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; }
}
  FindResponse除了具有一個表述目標服務終結點元數據的Endpoints屬性之外,還具有一個GetMessageSequence方法,該方法以EndpointDiscoveryMetadata對象作為輸入,返回一個System.ServiceModel.Discovery.DiscoveryMessageSequence對象。DiscoveryMessageSequence被稱為消息序列,涉及到定義在WS-Discovery中的一個重要的概念應用序列(Application Sequence/AppSequence)。簡單起見,我們可以這樣來理解:在采用廣播模式的服務發現截至無法確保消息的有序接收,即不能確保消息按照它被發送的順序被接收(先發先至),所以需要相應的序號封裝在一個被稱為AppSequence的報頭中被發送。DiscoveryMessageSequence類型定義如下,它的三個只讀屬性分別對應著AppSequence報頭的相應的屬性(Attribute),具體的含義請參考WS-Discovery規范。
public class DiscoveryMessageSequence : ...
{
    //其他成員
    public long InstanceId {get;  }
    public long MessageNumber {get; }
    public Uri SequenceId {get; }
}
  三、ResolveCriteria/ResolveResponse

  上面我們介紹了用于進行可用服務探測的Find/FindAsync操作的輸入和輸出,接下倆我們按照相同的方式來分析用于進行服務解析的Resolve/ResolveAsync操作的輸入和輸出。首先來介紹一下用于封裝匹配條件的ResolveCriteria類型,下面給出了它核心的屬性定義。

public class ResolveCriteria
{
    //其他成員
    public EndpointAddress Address { get; set; }
    public Collection<XElement> Extensions { get; }
    public TimeSpan Duration { get; set; }
}
  其中Address屬性表示被解析的服務的終結點地址,而Exntesions代表以XElement集合表示的擴展信息。Duration屬性表示Resolve操作執行的超時時限,即要求對于Resolve請求,在規定的時限內必須得到回復。如果沒有進行顯式設置,Durarion屬性采用默認值20秒。

  而作為Resolve/ResolveAsync輸出的ResolveResponse類型定義很簡單。它具有兩個核心的只讀屬性,代表被解析后的服務終結點元數據的EndpointDiscoveryMetadata屬性和代表消息序列的MessageSequence屬性。

public class ResolveResponse
{
    //其他成員
    public EndpointDiscoveryMetadata EndpointDiscoveryMetadata { get;}
    public DiscoveryMessageSequence MessageSequence { get;}
}
 
 

文章列表

arrow
arrow
    全站熱搜

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