Dieser Artikel wurde maschinell übersetzt.

WCF-Architektur

Discovery für den AppFabric-Dienstbus

Juval Lowy

Downloaden des Codebeispiels

In meinem Artikel Januar 2010 "Entdecken eines neuen WCF mit Discovery" (msdn.microsoft.com/magazine/ee335779), präsentiert ich die wertvolle Discovery-Funktion von Windows Communication Foundation (WCF) 4. WCF-Ermittlung ist im Grunde eine Intranet-orientierten Technik besteht keine Möglichkeit, Adressinformationen über das Internet zu übertragen.

Die Vorteile von dynamischen Adressen und Entkopplung Clients und Dienste auf der Achse Adresse würde noch einfach auch auf Dienste angewendet, die den Service Bus Clientaufrufe empfangen.

Glücklicherweise können Sie Ereignisse Relay Bindung User Datagram Protocol (UDP) Multicastadresse ersetzen anfordert und zur Ermittlung und Ankündigungen. This enables you to combine the benefit of easy deployment of discoverable services with the unhindered connectivity of the service bus. In diesem Artikel durchläuft einen kleinen Rahmen habe ich über den Bus Service Discovery unterstützen – Sie schalten auf Gleichwertigkeit mit der integrierten Unterstützung für die Erkennung in WCF – zusammen mit meiner Reihe von Hilfsklassen. Es dient auch als Beispiel für eigene Discovery-Mechanismus bereitstellen.

AppFabric Service Bus-Hintergrund

Wenn Sie mit der AppFabric Service Bus nicht vertraut sind, können Sie diese letzten Artikel lesen:

Lösungsarchitektur

Die integrierten WCF-Ermittlung sind standardmäßige Verträge für den Austausch von Discovery. Leider sind diese Verträge als intern definiert. Der erste Schritt in einer benutzerdefinierten Discovery-Mechanismus ist so definieren Sie die Verträge für Ermittlungsanfrage und Rückrufe verwendet. Ich definierte IServiceBusDiscovery Vertrag wie folgt:

[ServiceContract]
public interface IServiceBusDiscovery
{
  [OperationContract(IsOneWay = true)]
  void OnDiscoveryRequest(string contractName,string contractNamespace,
    Uri[] scopesToMatch,Uri replayAddress);
}

Die einzelnen Operation IServiceBusDiscovery wird von der Discovery-Endpunkt unterstützt. OnDiscoveryRequest kann die Kunden die Erforschung von Dienstendpunkten, die einen bestimmten Vertrag, wie bei normalen WCF. Die Clients können auch einen optionalen Satz von Bereichen entsprechen übergeben.

Dienste sollten den Discovery-Endpunkt über die Ereignisse Relay-Bindung unterstützen. Ein Client löst Anforderungen an Dienste, die unterstützen den Endpunkt Discovery, Anfordern der Dienste-Aufruf zurück an den Client bereitgestellt, Antwortadresse des.

Die Dienste Rückruf an den Client mithilfe der IServiceBusDiscoveryCallback, definiert als:

[ServiceContract]
public interface IServiceBusDiscoveryCallback
{
  [OperationContract(IsOneWay = true)]
  void DiscoveryResponse(Uri address,string contractName,
    string contractNamespace, Uri[] scopes);
}

Der Client stellt einen Endpunkt, deren Adresse der ReplayAddress-Parameter des OnDiscoveryRequest ist, IServiceBusDiscoveryCallback unterstützen. Die verwendete Bindung sollte die unidirektionale Relay-Bindung annähernd Unicast so weit wie möglich sein. Abbildung 1 ist der Ablauf der Erkennung dargestellt.

image: Discovery over the Service Bus

Abbildung 1 über den Bus Service Discovery

Abbildung 1 im erste Schritt wird ein Client Auslösen eines Ereignisses von Ermittlungsanfrage am Endpunkt Discovery IServiceBusDiscovery unterstützen. Dank an Ereignisse binden wird dieses Ereignis durch alle erkennbaren Dienste empfangen. Wenn ein Dienst den angeforderten Vertrag unterstützt, ruft wieder über den Service Bus (Schritt 2 unter Abbildung 1 ) an den Client. Nachdem der Client die-Endpunkt (oder Endpunkte) empfängt Adressen, die den Dienst aufrufen, wie bei einer regulären Service Bus-Aufruf (Schritt 3 in Abbildung 1 ) verläuft.

Erkennbaren Host

Offensichtlich ist viel Arbeit beteiligt, bei der Unterstützung von solchen einen Suchmechanismus besonders für den Dienst. Ich war in der Lage, mit meinem DiscoverableServiceHost, definiert als zu kapseln:

public class DiscoverableServiceHost : ServiceHost,...
{ 
  public const string DiscoveryPath = "DiscoveryRequests";

  protected string Namespace {get;}

  public Uri DiscoveryAddress {get;set;}

      public NetEventRelayBinding DiscoveryRequestBinding {get;set;}
      
  public NetOnewayRelayBinding DiscoveryResponseBinding {get;set;}

  public DiscoverableServiceHost(object singletonInstance,
    params Uri[] baseAddresses);
  public DiscoverableServiceHost(Type serviceType,
    params Uri[] baseAddresses);
}

Neben der Suche veröffentlicht DiscoverableServiceHost immer die Dienstendpunkte der Service Bus-Registrierung. Zur Ermittlung, genau wie bei normalen WCF-Erkennung zu aktivieren, müssen Sie eine Discovery-Verhalten und einer WCF-Discovery-Endpunkt hinzufügen. Dies ist bewusst, um vermeiden, noch ein weiteres Steuerelement-Schalter hinzufügen sowohl einer versehen eine einzelne, konsistente Konfiguration, wo Sie aktivieren oder Deaktivieren von allen Modi der Ermittlung.

Verwenden Sie DiscoverableServiceHost wie jeden anderen Dienst auf dem Bus Dienst verlassen:

Uri baseAddress = 
  new Uri("sb://MyServiceNamespace.servicebus.windows.
net/MyService/");

ServiceHost host = new DiscoverableServiceHost(typeof(MyService),baseAddress);
           
// Address is dynamic
host.AddServiceEndpoint(typeof(IMyContract),new NetTcpRelayBinding(),
  Guid.NewGuid().ToString());
 
// A host extension method to pass creds to behavior
host.SetServiceBusCredentials(...);

host.Open();

Beachten Sie, dass bei der Verwendung von Discovery Service-Adresse vollständig dynamisch sein kann.

Abbildung 2 bietet partielle Implementierung von relevanten DiscoverableServiceHost-Elementen.

Abbildung 2 Implementieren DiscoverableServiceHost (teilweise)

public class DiscoverableServiceHost : ServiceHost,...
{  
  Uri m_DiscoveryAddress;
  ServiceHost m_DiscoveryHost;
    
  // Extracts the service namespace out of the endpoints or base addresses
  protected string Namespace
  {
    get
    {...}
  }

  bool IsDiscoverable
  {
    get
    {
      if(Description.Behaviors.Find<ServiceDiscoveryBehavior>() != null)
      {
        return Description.Endpoints.Any(endpoint => 
          endpoint is DiscoveryEndpoint);
      }
      return false;
    }
  }
   
  public Uri DiscoveryAddress
  {
    get
    {
      if(m_DiscoveryAddress == null)
      {
        m_DiscoveryAddress =  
          ServiceBusEnvironment.CreateServiceUri("sb",Namespace,DiscoveryPath);
      }
      return m_DiscoveryAddress;
    }
    set
    {
      m_DiscoveryAddress = value;
    }
  }

  public DiscoverableServiceHost(Type serviceType,params Uri[] 
    baseAddresses) : base(serviceType,baseAddresses)
  {}

  void EnableDiscovery()
  {
    // Launch the service to monitor discovery requests
    DiscoveryRequestService discoveryService = 
      new DiscoveryRequestService(Description.Endpoints.ToArray());

    discoveryService.DiscoveryResponseBinding = DiscoveryResponseBinding;

    m_DiscoveryHost = new ServiceHost(discoveryService);

    m_DiscoveryHost.AddServiceEndpoint(typeof(IServiceBusDiscovery),
      DiscoveryRequestBinding, DiscoveryAddress.AbsoluteUri);

     m_DiscoveryHost.Open();
  }

  protected override void OnOpening()
  {
    if(IsDiscoverable)
    {
      EnableDiscovery();
    }
     
    base.OnOpening();
  }
  protected override void OnClosed()
  {
    if(m_DiscoveryHost != null)
    {
      m_DiscoveryHost.Close();
    }

    base.OnClosed();
  }

  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,...]
  class DiscoveryRequestService : IServiceBusDiscovery
  {
    public DiscoveryRequestService(ServiceEndpoint[] endpoints);

    public NetOnewayRelayBinding DiscoveryResponseBinding
    {get;set;}
  }   
}

Hilfseigenschaft des DiscoverableServiceHost IsDiscoverable zurück, nur, wenn der Dienst eine Discovery-Verhalten und mindestens eine Discovery-Endpunkt verfügt. DiscoverableServiceHost überschreibt die Methode OnOpening von ServiceHost. Wenn der Dienst erkannt werden, ruft OnOpening EnableDiscovery-Methode.

EnableDiscovery ist das Kernstück der DiscoverableServiceHost. Er erstellt einen internen Host für eine private Singletonklasse DiscoveryRequestService aufgerufen (siehe Abbildung 3 ).

Abbildung 3 Die DiscoveryRequestService-Klasse (teilweise)

public class DiscoverableServiceHost : ServiceHost
{
  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
    UseSynchronizationContext = false)]
  class DiscoveryRequestService : IServiceBusDiscovery
  {
    readonly ServiceEndpoint[] Endpoints;

    public NetOnewayRelayBinding DiscoveryResponseBinding
    {get;set;}

    public DiscoveryRequestService(ServiceEndpoint[] endpoints)
    {
      Endpoints = endpoints;
    }

    void IServiceBusDiscovery.OnDiscoveryRequest(string contractName,
      string contractNamespace, Uri[] scopesToMatch, Uri responseAddress)
    {
      ChannelFactory<IServiceBusDiscoveryCallback> factory = 
        new ChannelFactory<IServiceBusDiscoveryCallback>(
        DiscoveryResponseBinding, new EndpointAddress(responseAddress));

      IServiceBusDiscoveryCallback callback = factory.CreateChannel();

      foreach(ServiceEndpoint endpoint in Endpoints)
      {
        if(endpoint.Contract.Name == contractName && 
          endpoint.Contract.Namespace == contractNamespace)
        {
          Uri[] scopes = DiscoveryHelper.LookupScopes(endpoint);

          if(scopesToMatch != null)
          {
            bool scopesMatched = true;
            foreach(Uri scope in scopesToMatch)
            {
              if(scopes.Any(uri => uri.AbsoluteUri == scope.AbsoluteUri) 
                == false)
              {
                scopesMatched = false;
                break;
              }
            }
            if(scopesMatched == false)
            {
              continue;
            }
          }
          callback.DiscoveryResponse(endpoint.Address.Uri,contractName,
            contractNamespace,scopes);
        }
      }
      (callback as ICommunicationObject).Close();
    }
  }   
}

public static class DiscoveryHelper
{
  static Uri[] LookupScopes(ServiceEndpoint endpoint)
  {
    Uri[] scopes = new Uri[]{};
    EndpointDiscoveryBehavior behavior = 
      endpoint.Behaviors.Find<EndpointDiscoveryBehavior>();
    if(behavior != null)
    {
      if(behavior.Scopes.Count > 0)
      {
        scopes = behavior.Scopes.ToArray();
      }
    }
    return scopes;
  }
  // More members
}

Der DiscoveryRequestService-Konstruktor akzeptiert die Dienstendpunkte für das Überwachen von Suchanforderungen werden muss (Dies sind im Wesentlichen die Endpunkte des DiscoverableServiceHost).

EnableDiscovery fügt anschließend an den Host einen Endpunkt implementieren IServiceBusDiscovery, da DiscoveryRequestService auf Discovery-Anforderungen von Clients reagiert. Die Adresse des Endpunkts Discovery wird der URI “ DiscoveryRequests ” unter dem Dienst-Namespace. Jedoch können vor dem Öffnen auf anderen URI, die mithilfe der Eigenschaft DiscoveryAddress DiscoverableServiceHost ändern. DiscoverableServiceHost schließen schließt auch den Host für den Discovery-Endpunkt.

Abbildung 3 Listet die DiscoveryRequestService-Implementierung.

OnDiscoveryRequest erstellt zunächst einen Proxy einen Rückruf für die Ermittlung von Clients. Die verwendete Bindung ist eine einfache NetOnewayRelayBinding, aber Sie können steuern, indem die Eigenschaft DiscoveryResponseBinding. Beachten Sie, dass DiscoverableServiceHost eine entsprechende Eigenschaft nur für diesen Zweck. OnDiscoveryRequest durchläuft dann die Auflistung von Endpunkten für den Konstruktor bereitgestellten. Für jeden Endpunkt überprüft er, dass der Vertrag den angeforderten Vertrag in der Discovery-Anforderung entspricht. Wenn der Vertrag übereinstimmt, wird OnDiscoveryRequest schlägt die Bereiche, die dem Endpunkt zugeordnet und stellt sicher, dass die Bereiche der optionale Bereiche in der Discovery-Anforderung entsprechen. Schließlich ruft OnDiscoveryRequest wieder den Client mit der Adresse, Vertrag und Umfang des Endpunkts.

Discovery-Client

Für den Client die Hilfsklasse ServiceBusDiscoveryClient, schrieb ich als definiert:

public class ServiceBusDiscoveryClient : ClientBase<IServiceBusDiscovery>,...
{         
  protected Uri ResponseAddress
  {get;}

  public ServiceBusDiscoveryClient(string serviceNamespace,string secret);

  public ServiceBusDiscoveryClient(string endpointName);
  public ServiceBusDiscoveryClient(NetOnewayRelayBinding binding,
    EndpointAddress address);

   public FindResponse Find(FindCriteria criteria);
}

Die WCF-DiscoveryClient ServiceBusDiscoveryClient nachgebildet und fast genauso, wie Abbildung 4 verwendet hat.

Abbildung 4 verwenden ServiceBusDiscoveryClient

string serviceNamespace = "...";
string secret = "...";

ServiceBusDiscoveryClient discoveryClient = 
  new ServiceBusDiscoveryClient(serviceNamespace,secret);
         
FindCriteria criteria = new FindCriteria(typeof(IMyContract));
FindResponse discovered = discoveryClient.Find(criteria);
discoveryClient.Close();
        
EndpointAddress address = discovered.Endpoints[0].Address;
Binding binding = new NetTcpRelayBinding();
ChannelFactory<IMyContract> factory = 
  new ChannelFactory<IMyContract> (binding,address);
// A channel factory extension method to pass creds to behavior
factory.SetServiceBusCredentials(secret);

IMyContract proxy = factory.CreateChannel();
proxy.MyMethod();
(proxy as ICommunicationObject).Close();

ServiceBusDiscoveryClient handelt es sich um einen Proxy für den Endpunkt des IServiceBusDiscovery Discovery-Ereignisse. Clients verwenden die Ermittlungsanfrage an erkennbaren Dienste auslösen. Die Endpunktadresse Discovery wird standardmäßig “ DiscoveryRequests ”, aber Sie können eine andere Adresse eines der Konstruktoren, die einen Endpunktname oder eine Endpunktadresse angeben. Sie verwenden eine einfache Instanz NetOnewayRelayBinding für den Endpunkt Discovery, aber Sie können angeben, dass eine andere Bindung eines der Konstruktoren, die einen Endpunktnamen oder eine Bindungsinstanz. ServiceBusDiscoveryClient unterstützt Kardinalität und Discovery-Timeouts, genau wie DiscoveryClient.

Abbildung 5 wird teilweise Implementierung ServiceBusDiscoveryClient.

Abbildung 5 Implementieren ServiceBusDiscoveryClient (teilweise)

public class ServiceBusDiscoveryClient : ClientBase<IServiceBusDiscovery> 
{         
   protected Uri ResponseAddress
   {get;private set;}

   public ServiceBusDiscoveryClient(string endpointName) : base(endpointName)
   {
      string serviceNamespace =  
        ServiceBusHelper.ExtractNamespace(Endpoint.Address.Uri);
      ResponseAddress = ServiceBusEnvironment.CreateServiceUri(
        "sb",serviceNamespace,"DiscoveryResponses/"+Guid.NewGuid());
    }

   public FindResponse Find(FindCriteria criteria)
   {
      string contractName = criteria.ContractTypeNames[0].Name;
      string contractNamespace = criteria.ContractTypeNames[0].Namespace;

      FindResponse response = DiscoveryHelper.CreateFindResponse();

      ManualResetEvent handle = new ManualResetEvent(false);

     Action<Uri,Uri[]> addEndpoint = (address,scopes)=>
     {
       EndpointDiscoveryMetadata metadata = new EndpointDiscoveryMetadata();
       metadata.Address = new EndpointAddress(address);
         if(scopes != null)
         {
           foreach(Uri scope in scopes)
             {
               metadata.Scopes.Add(scope);
             }
         }
         response.Endpoints.Add(metadata);
                                         
         if(response.Endpoints.Count >= criteria.MaxResults)
         {
           handle.Set();
         }
     };

     DiscoveryResponseCallback callback = 
       new DiscoveryResponseCallback(addEndpoint);

     ServiceHost host = new ServiceHost(callback);
        

     host.AddServiceEndpoint(typeof(IServiceBusDiscoveryCallback),
       Endpoint.Binding,ResponseAddress.AbsoluteUri);

     host.Open();

     try
     {         
       DiscoveryRequest(criteria.ContractTypeNames[0].Name,
         criteria.ContractTypeNames[0].Namespace,
         criteria.Scopes.ToArray(),ResponseAddress);

       handle.WaitOne(criteria.Duration);
     }
     catch
     {}
     finally
     {
       host.Abort();
     }
     return response;
   }
   void DiscoveryRequest(string contractName,string contractNamespace,
     Uri[] scopesToMatch,Uri replayAddress)
   {
     Channel.OnDiscoveryRequest(contractName,contractNamespace,
       scopesToMatch, replayAddress);
   }

   [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
     UseSynchronizationContext = false)]
   class DiscoveryResponseCallback : IServiceBusDiscoveryCallback
   {
     readonly Action<Uri,Uri[]> Action;

     public DiscoveryResponseCallback(Action<Uri,Uri[]> action)
     {
       Action = action;
     }
     public void DiscoveryResponse(Uri address,string contractName,
       string contractNamespace,Uri[] scopes)
     {
       Action(address,scopes);
     }
   }
}

public static class DiscoveryHelper
{
  internal static FindResponse CreateFindResponse()
  {
    Type type = typeof(FindResponse);

    ConstructorInfo constructor =  

      type.GetConstructors(BindingFlags.Instance|BindingFlags.NonPublic)[0];

     return constructor.Invoke(null) as FindResponse;
  }  
  // More members
}

Die Find-Methode muss eine Möglichkeit, Rückrufe von ermittelte Dienste erhalten haben. Zu diesem Zweck jedes Mal, wenn Sie aufgerufen wird, suchen öffnet und schließt einen Host für eine interne synchronisierten Singletonklasse DiscoveryResponseCallback aufgerufen. Suchen der Host einen Endpunkt IServiceBusDiscoveryCallback Unterstützung hinzugefügt ist. Der DiscoveryResponseCallback-Konstruktor akzeptiert einen Delegaten des Typs < Uri Uri [] >-Aktion. Jedes Mal, wenn ein Dienst wieder antwortet, ruft die Implementierung von DiscoveryResponse Delegat, ihn mit der ermittelten Adresse und des Umfangs bereit. Die Find-Methode verwendet einen Lambda-Ausdruck, um die Antworten in einer Instanz der FindResponse aggregieren. Es gibt leider keinen öffentlicher Konstruktor für FindResponse, daher suchen die CreateFindResponse-Methode des DiscoveryHelper, die Reflektion wiederum verwendet verwendet, instanziiert. Suche erstellt außerdem eine Ereigniszugriffsnummer Wartemöglichkeit. Der Lambda-Ausdruck-Signale, die verarbeitet werden, wenn die Kardinalität erfüllt ist. Nach dem Aufruf von DiscoveryRequest suchen wartet, für die Handles signalisiert werden oder für die Ermittlung Dauer ablaufen und dann den Host zum Stoppen der Verarbeitung keine Discovery-Antworten in Bearbeitung abgebrochen wird.

Weitere clientseitigen Hilfsklassen

Obwohl schrieb ich ServiceBusDiscoveryClient zum DiscoveryClient funktionell identisch sein, würden Sie eine optimierte Discovery-Erfahrung von meinem ServiceBusDiscoveryHelper angeboten nutzen:

public static class ServiceBusDiscoveryHelper
{
  public static EndpointAddress DiscoverAddress<T>(
    string serviceNamespace,string secret,Uri scope = null);

  public static EndpointAddress[] DiscoverAddresses<T>(
    string serviceNamespace,string secret,Uri scope = null);
   
  public static Binding DiscoverBinding<T>(
    string serviceNamespace,string secret,Uri scope = null);
}

DiscoverAddress <T>einen Dienst ermittelt mit einer Kardinalität, eine DiscoverAddresses <T>alle verfügbaren Dienstendpunkte (Kardinalität von allen) und DiscoverBinding <T> ermitteltverwendet den Metadaten Dienstendpunkt, um die Endpunkt-Bindung zu ermitteln. Ähnlich wie, habe ich die ServiceBusDiscoveryFactory-Klasse definiert:

public static class ServiceBusDiscoveryFactory
{
  public static T CreateChannel<T>(string serviceNamespace,string secret,
    Uri scope = null) where T : class;
   
  public static T[] CreateChannels<T>(string serviceNamespace,string secret,
    Uri scope = null) where T : class;
}

CreateChannel <T>nimmt die Kardinalität, und verwendet den Metadatenendpunkt des Dienstes Adresse und zum Erstellen des Proxys verwendete Bindung ab. CreateChannels <T>Proxys auf alle ermittelten Dienste mit allen ermittelten Metadaten-Endpunkten erstellt.

Ankündigungen

Zur Unterstützung von Ankündigungen können Sie erneut die Ereignisse Relay-Bindung verwenden, um UDP-multicast zu ersetzen. Zunächst definiert habe ich den IServiceBusAnnouncements Ankündigung Vertrag:

[ServiceContract]
public interface IServiceBusAnnouncements
{
  [OperationContract(IsOneWay = true)]
  void OnHello(Uri address, string contractName,
    string contractNamespace, Uri[] scopes);

  [OperationContract(IsOneWay = true)]
  void OnBye(Uri address, string contractName, 
    string contractNamespace, Uri[] scopes);
}

Wie im mit Abbildung 6, dieses Mal ist es den Clients einen Ereignis Bindung Endpunkt verfügbar zu machen, und Überwachen Sie die Ankündigungen einrichten.

image: Availability Announcements over the Service Bus

Abbildung 6 Verfügbarkeit Ankündigungen über den Service Bus

Kündigen Sie die Dienste werden deren Verfügbarkeit (über die unidirektionale Relay-Bindung), deren Adresse (Schritt 1 Abbildung 6 ) und den Clients werden fortgesetzt werden (Schritt 2 unter Abbildung 6 ) aufrufen.

Service-Side-Ankündigungen

Meine DiscoveryRequestService unterstützt Ankündigungen:

public class DiscoverableServiceHost : ServiceHost,...
{
   public const string AnnouncementsPath = "AvailabilityAnnouncements";   

   public Uri AnnouncementsAddress
   {get;set;}
      
   public NetOnewayRelayBinding AnnouncementsBinding
   {get;set;}

  // More members 
}

Allerdings wird nicht mit den integrierten WCF-Ankündigungen auf Gleichwertigkeit standardmäßig er seine Verfügbarkeit kündigen. Um Ankündigungen zu aktivieren, müssen Sie einen Endpunkt Ankündigung mit dem Discovery-Verhalten zu konfigurieren. In most cases, this is all you’ll need to do. DiscoveryRequestService will fire its availability events on the “AvailabilityAnnouncements” URI under the service namespace. Sie können diesen Standardwert ändern, indem Sie AnnouncementsAddress-Eigenschaft festlegen, bevor Sie den Host öffnet. Standardmäßig mit eine einfachen unidirektionale Relay-Bindung werden die Ereignisse ausgelöst werden, jedoch können Sie eine Alternative mithilfe der AnnouncementsBinding-Eigenschaft vor dem Öffnen des Hosts bereitstellen. DiscoveryRequestService löst seine Verfügbarkeitsereignisse asynchron, um zu vermeiden, blockieren Vorgänge beim Öffnen und Schließen des Hosts. Abbildung 7 zeigt die Ankündigung Support-Elemente DiscoveryRequestService.

Abbildung 7 unterstützen Ankündigungen mit DiscoveryRequestService

public class DiscoverableServiceHost : ServiceHost,...
{
   Uri m_AnnouncementsAddress;

   bool IsAnnouncing
   {
      get
      {
         ServiceDiscoveryBehavior behavior = 
           Description.Behaviors.Find<ServiceDiscoveryBehavior>();
         if(behavior != null)
         {
           return behavior.AnnouncementEndpoints.Any();
         }
         return false;
      }
   }

   public Uri AnnouncementsAddress
   {
      get
      {
        if(m_AnnouncementsAddress == null)
        {
          m_AnnouncementsAddress = ServiceBusEnvironment.
CreateServiceUri("sb",Namespace,AnnouncementsPath);
        }
        return m_AnnouncementsAddress;
     }
     set
     {
       m_AnnouncementsAddress = value;
     }
  }

  IServiceBusAnnouncements CreateAvailabilityAnnouncementsClient()
  {    
    ChannelFactory<IServiceBusAnnouncements> factory = 
      new ChannelFactory<IServiceBusAnnouncements>(
      AnnouncementsBinding,new EndpointAddress(AnnouncementsAddress));

    return factory.CreateChannel();
  }

   protected override void OnOpened()
   {
      base.OnOpened();

      if(IsAnnouncing)
      {
        IServiceBusAnnouncements proxy =  
          CreateAvailabilityAnnouncementsClient();
        PublishAvailabilityEvent(proxy.OnHello);
      }
   }

   protected override void OnClosed()
   {
     if(IsAnnouncing)
     {
       IServiceBusAnnouncements proxy = 
         CreateAvailabilityAnnouncementsClient();
       PublishAvailabilityEvent(proxy.OnBye);
     }
     ...
}

  void PublishAvailabilityEvent(Action<Uri,string,string,Uri[]> notification)
  {
    foreach(ServiceEndpoint endpoint in Description.Endpoints)
    {
      if(endpoint is DiscoveryEndpoint || endpoint is 
        ServiceMetadataEndpoint)
      {
        continue;
      }
      Uri[] scopes = LookupScopes(endpoint);

      WaitCallback fire = delegate
      {
        try
        {
          notification(endpoint.Address.Uri, endpoint.Contract.Name,
            endpoint.Contract.Namespace, scopes);
          (notification.Target as ICommunicationObject).Close();

        }
        catch
        {}
      };
      ThreadPool.QueueUserWorkItem(fire); 
    }
  }
}

Die Hilfsmethode CreateAvailabilityAnnouncementsClient verwendet eine Kanalfactory um einen Proxy für den Endpunkt des IServiceBusAnnouncements Ankündigungen Ereignisse erstellen. Nach der öffnenden und vor dem Schließen DiscoveryRequestService löst es die Benachrichtigungen. DiscoveryRequestService überschreibt OnOpened sowohl OnClosed Methoden von ServiceHost. Wenn der Host konfiguriert ist, ankündigen zu können, rufen Sie OnOpened und OnClosed CreateAvailabilityAnnouncementsClient, erstellen einen Proxy, und übergeben Sie an die PublishAvailabilityEvent-Methode, um das Ereignis asynchron ausgelöst. Da der Vorgang Auslösen des Ereignisses für die Grußpaket- und die Bye Ankündigungen identisch ist und der einzige Unterschied, welche Methode IServiceBusAnnouncements ist aufgerufen wird, akzeptiert PublishAvailabilityEvent einen Delegaten für die Zielmethode. Für jeden Endpunkt des DiscoveryRequestService PublishAvailabilityEvent sucht, die diesem Endpunkt zugeordnete Bereiche und Warteschlangen, bis die Ankündigung an den Threadpool des Microsoft .NET Framework mit einer anonyme WaitCallback-Methode. Die anonyme Methode bereitgestellte Delegat aufgerufen und schließt dann den zugrunde liegenden Ziel-Proxy.

Empfangen von Ankündigungen

Ich könnte haben imitiert die AnnouncementService WCF bereitgestellte wie beschrieben in meinem Artikel Januar, aber eine lange Liste der Dinge, die ich bei meinen AnnouncementSink <T> verbessert haben und einen Fall, in dem Sie AnnouncementService zugunsten der AnnouncementSink <T> verwenden lieber, nicht angezeigt. Ich wollte auch nutzen und das Verhalten der AnnouncementSink <T> wiederverwendenund ihrer Basisklasse.

Aus diesem Grund habe ich für den Client ServiceBusAnnouncementSink <T>, definiert als:

[ServiceBehavior(UseSynchronizationContext = false,
  InstanceContextMode = InstanceContextMode.Single)]
public class ServiceBusAnnouncementSink<T> : AnnouncementSink<T>,
   IServiceBusAnnouncements, where T : class
{
  public ServiceBusAnnouncementSink(string serviceNamespace,string secret);

  public ServiceBusAnnouncementSink(string serviceNamespace,string owner,
    string secret);
  public Uri AnnouncementsAddress get;set;}

  public NetEventRelayBinding AnnouncementsBinding {get;set;}
}

Die Konstruktoren von ServiceBusAnnouncementSink <T>ist den Dienst-Namespace erforderlich.

ServiceBusAnnouncementSink <T>IServiceBusAnnouncements unterstützt als Singleton selbst gehosteten. ServiceBusAnnouncementSink <T>auch veröffentlicht selbst in der Registrierung für Service Bus. ServiceBusAnnouncementSink <T>abonniert werden standardmäßig die Verfügbarkeit-Ankündigungen “ AvailabilityAnnouncements ” URI unter dem Dienst-Namespace. Sie können mit ändern (vor dem Öffnen) durch Festlegen der Eigenschaft AnnouncementsAddress. ServiceBusAnnouncementSink <T>verwendet (standardmäßig) eine einfache NetEventRelayBinding die Benachrichtigungen empfangen, aber Sie können durch Festlegen der AnnouncementsBinding vor dem Öffnen von ServiceBusAnnouncementSink <T> ändern. Die ServiceBusAnnouncementSink <T>-clientskann der Stellvertreter des AnnouncementSink <T> abonnierenDie Ankündigungen zu empfangen oder Sie einfach die Adresse in der Basisadresse Container zugreifen können. Ein Beispiel finden Sie unter Abbildung 8 .

Abbildung 8 empfangen Ankündigungen

class MyClient 
{
   AddressesContainer<IMyContract> m_Addresses;

   public MyClient()
   {
      string serviceNamespace = "...";
      string secret = "...";

      m_Addresses = new ServiceBusAnnouncementSink<IMyContract>(
        serviceNamespace,secret);

      m_Addresses.Open();

      ...
}
   public void OnCallService()
   {  
      EndpointAddress address = m_Addresses[0];

      IMyContract proxy = ChannelFactory<IMyContract>.CreateChannel(
         new NetTcpRelayBinding(),address);
      proxy.MyMethod();
      (proxy as ICommunicationObject).Close();
   }
   ...
}

Abbildung 9 wird die partielle Implementierung der ServiceBusAnnouncementSink <T>ohne einige der Fehlerbehandlung.

Abbildung 9 ServiceBusAnnouncementSink <T> implementieren(Partiell)

[ServiceBehavior(UseSynchronizationContext = false,
   InstanceContextMode = InstanceContextMode.Single)]
public class ServiceBusAnnouncementSink<T> : AnnouncementSink<T>,
  IServiceBusAnnouncements
{
   Uri m_AnnouncementsAddress;

   readonly ServiceHost Host;
   readonly string ServiceNamespace;
   readonly string Owner;
   readonly string Secret;

   public ServiceBusAnnouncementSink(string serviceNamespace,
     string owner,string secret)
   {
      Host = new ServiceHost(this);
      ServiceNamespace = serviceNamespace;
      Owner = owner;
      Secret = secret;
   }

   public Uri AnnouncementsAddress
   {
      get
      {
         if(m_AnnouncementsAddress == null)
         {
           m_AnnouncementsAddress = 
             ServiceBusEnvironment.CreateServiceUri(
             "sb",ServiceNamespace,
             DiscoverableServiceHost.AnnouncementsPath);
         }
         return m_AnnouncementsAddress;
      }
      set
      {
        m_AnnouncementsAddress = value;
      }
   }
   public override void Open()
   {
      base.Open();

      Host.AddServiceEndpoint(typeof(IServiceBusAnnouncements),
        AnnouncementsBinding, AnnouncementsAddress.AbsoluteUri);
      Host.SetServiceBusCredentials(Owner,Secret);
      Host.Open();
   }
   public override void Close()
   {
      Host.Close();

      base.Close();
   }

   void IServiceBusAnnouncements.OnHello(Uri address,string contractName,
     string contractNamespace,Uri[] scopes)
   {
      AnnouncementEventArgs args = 
        DiscoveryHelper.CreateAnnouncementArgs(
        address,contractName,contractNamespace,scopes);
      OnHello(this,args); //In the base class AnnouncementSink<T>
   }

   void IServiceBusAnnouncements.OnBye(Uri address,string contractName,
     string contractNamespace,Uri[] scopes)
   {
     AnnouncementEventArgs args = DiscoveryHelper.CreateAnnouncementArgs(
       address,contractName,contractNamespace,scopes);        
     OnBye(this,args); //In the base class AnnouncementSink<T>
   }
}

public static class DiscoveryHelper
{
  static AnnouncementEventArgs CreateAnnouncementArgs(Uri address,
    string contractName, string contractNamespace, Uri[] scopes)
  {
    Type type = typeof(AnnouncementEventArgs);
    ConstructorInfo constructor = 
      type.GetConstructors(BindingFlags.Instance|BindingFlags.NonPublic)[0];

    ContractDescription contract = 
      new ContractDescription(contractName,contractNamespace);

    ServiceEndpoint endpoint = 
      new ServiceEndpoint(contract,null,new EndpointAddress(address));
    EndpointDiscoveryMetadata metadata = 
      EndpointDiscoveryMetadata.FromServiceEndpoint(endpoint);

    return constructor.Invoke(
      new object[]{null,metadata}) as AnnouncementEventArgs;
  }
  // More members
}

Der Konstruktor der ServiceBusAnnouncementSink <T>sich selbst als Singleton hostet, und speichert die Dienst-Namespace. Beim Öffnen von ServiceBusAnnouncementSink <T> hinzugefügt seinen eigenen Host einen Endpunkt IServiceBusAnnouncements unterstützen. Die Implementierung von Ereignisbehandlungsmethoden IServiceBusAnnouncements erstellt eine Instanz des AnnouncementEventArgs füllen es mit dem angekündigten Dienstadresse, Vertrag und Bereiche, und ruft dann die Basisklassenimplementierung der jeweiligen Ankündigung Methoden, als ob Sie mithilfe regulärer WCF aufgerufen wurde. Diese beiden füllt die Basisklasse der AddressesContainer <T>und wird die entsprechenden AnnouncementSink <T>-Ereignisse ausgelöst. Beachten Sie, dass zum Erstellen einer Instanz des AnnouncementEventArgs Reflektion aufgrund von fehlenden einen öffentlichen Konstruktor verwenden müssen.

Der Metadaten-Explorer

Zur Unterstützung des Service Bus erweitert ich mit meiner Unterstützung für die Ermittlung für den Service Bus, Discovery-Feature des Metadaten-Explorer-Tools (in früheren Artikeln angezeigt) .Wenn Sie die Discover Schaltfläche (siehe Abbildung 10),die Metadaten für jeden Dienst Namespace Anmeldeinformationen wurden bereits bereitgestellt werden, für die im Metadaten-Explorer zu ermitteln versucht exchange-Endpunkte von Webdiensten sichtbar und die erkannten Endpunkte in der Struktur anzuzeigen.

image: Configuring Discovery over the Service Bus

Abbildung 10 Konfigurieren über den Bus Service Discovery

Der Metadaten-Explorer werden standardmäßig mit den URI “ DiscoveryRequests ” unter dem Dienst-Namespace. Ändern Sie diesen Pfad, indem Sie Service Bus im Menü dann Discovery, um das Dialogfeld Configure AppFabric-Bus-Dienstermittlung anzuzeigen (siehe Abbildung 10 ).

Für jeden Dienst-Namespace von Interesse kann im Dialogfeld den gewünschten relativen Pfad, der den Endpunkt des Discovery-Ereignisse in das Textfeld Path Discovery konfigurieren.

Der Metadaten-Explorer unterstützt auch Ankündigungen von Dienstendpunkten Bus Metadaten austauschen. Aktiviert das Empfangen einer Benachrichtigung Verfügbarkeit, Discovery-Konfigurationsdialogfeld zu bringen, und aktivieren Sie das Kontrollkästchen Enable unter Verfügbarkeit Ankündigungen. Der Metadaten-Explorer wird standardmäßig auf “ AvailabilityAnnouncements ” URI unter dem angegebenen Dienst-Namespace verwenden kann, jedoch konfiguriert für jeden Dienst Namespace andere gewünschte Pfad für den Endpunkt Ankündigungen.

Die Unterstützung für Ankündigungen im Metadaten-Explorer macht es einfach, praktische und nützliche Service Bus Überwachungstool.

Juval Lowy ist Softwarearchitekt mit .NET und Architektur, Schulung und Beratung IDesign. Dieser Artikel enthält Auszüge aus seinem letzten Buch “ Programming WCF Services 3rd Edition ” (O’Reilly 2010). Er ist außerdem Microsoft Regional Director für das Silicon Valley. Wenden Sie sich an unter idesign.net-Lowy.

Dank an den folgenden technischen Experten für die Überprüfung der in diesem Artikel: Wade Wegner