Este artigo foi traduzido por máquina.

Arquitetura do WCF

Descoberta de barramento de serviço AppFabric

Juval Lowy

Baixe o código de exemplo

Em meu artigo de janeiro de 2010 “ descobrir um novo WCF com descoberta ” (msdn.microsoft.com/magazine/ee335779 de ), apresentei o recurso de descoberta importantes do Windows Communication Foundation (WCF) 4. Descoberta do WCF é fundamentalmente uma técnica orientado a intranet, pois não há nenhuma maneira de transmitir informações de endereço na Internet.

Ainda os benefícios de endereços dinâmicos e serviços no eixo de endereço e clientes decoupling aplicaria tão bem para os serviços que dependem de barramento de serviço para receber chamadas de clientes.

Felizmente, você pode usar os eventos de ligação de retransmissão a substituição de protocolo (UDP) multicast solicitações e forneça para detecção e anúncios. Isso permite combinar a vantagem de facilitar a implantação dos serviços detectáveis com a conectividade unhindered do barramento de serviço. Este artigo orienta em meio a uma pequena estrutura que escrevi para dar suporte à descoberta em barramento de serviço — trazendo-o em comparação com o suporte interno para a descoberta no WCF — juntamente com o meu conjunto de classes auxiliares. Ela também serve como um exemplo de implementar seu próprio mecanismo de descoberta.

Plano de fundo de barramento de serviço AppFabric

Se você não estão familiarizados com o barramento de serviços de AppFabric, você pode ler estes artigos anteriores:

Arquitetura de solução

Para a detecção interna do WCF, existem contratos padrão para a troca de descoberta. Infelizmente, esses contratos definidos como internos. A primeira etapa de um mecanismo de detecção personalizada é definir os contratos usados para a solicitação de descoberta e retornos de chamada. Defini o contrato IServiceBusDiscovery da seguinte maneira:

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

IServiceBusDiscovery a única operação é compatível com o ponto de extremidade de descoberta. OnDiscoveryRequest permite que os clientes descobrir os pontos de extremidade de serviço oferecer suporte a um contrato específico, assim como acontece com o WCF regular. Os clientes também podem passar um conjunto opcional de escopos para coincidir.

Serviços devem oferecer suporte o ponto de extremidade de descoberta pela ligação de retransmissão de eventos. Um cliente dispara solicitações de serviços que oferecem suporte à descoberta de ponto de extremidade, que solicita a chamada de serviços ao cliente do fornecido um endereço de resposta.

Os serviços de retorno de chamada para o cliente usando IServiceBusDiscoveryCallback, definido como:

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

O cliente fornece um ponto de extremidade com suporte IServiceBusDiscoveryCallback cujo endereço é o parâmetro replayAddress de OnDiscoveryRequest. A ligação usada deve ser a ligação de retransmissão unidirecionais para aproximar ao máximo possível de unicast. A Figura 1 representa a seqüência de descoberta.

image: Discovery over the Service Bus

Figura 1 de de descoberta de barramento de serviços

A primeira etapa no do Figura 1 é um cliente, disparando um evento de solicitação de descoberta do ponto de extremidade de descoberta IServiceBusDiscovery de suporte. Graças aos eventos de ligação, este evento é recebido por todos os serviços localizáveis. Se um serviço aceita o contrato solicitado, ele responde ao cliente por meio do barramento de serviço (etapa 2 de do Figura 1). Depois que o cliente recebe o ponto de extremidade de serviço (ou pontos de extremidade) endereços, ele prosseguirá para chamar o serviço como com uma chamada de barramento de serviço normal (etapa 3 em do Figura 1).

Host localizável

É claro que muito trabalho está envolvido no suporte como um mecanismo de descoberta, especialmente para o serviço. Fui capaz de encapsular que o meu DiscoverableServiceHost, definido como:

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);
}

Além de descoberta, DiscoverableServiceHost sempre publica os pontos de extremidade de serviço no registro de barramento de serviço. Para ativar a descoberta, assim como acontece com detecção regular do WCF, você deve adicionar um comportamento de detecção e um ponto de extremidade de descoberta do WCF. Isso é proposital, para evitar adicionar outra opção de controle tanto em um colocar uma única configuração consistente em que você ativa ou desativar todos os modos de descoberta.

Você pode usar DiscoverableServiceHost como qualquer outro serviço que depender de barramento de serviço:

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();

Observe que, ao usar a detecção, o endereço do serviço pode ser totalmente dinâmico.

A Figura 2 fornece a implementação parcial de elementos relevantes do DiscoverableServiceHost.

A Figura 2 de implementação DiscoverableServiceHost (parcial)

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;}
  }   
}

A propriedade auxiliar IsDiscoverable de DiscoverableServiceHost retorna true somente se o serviço tem um comportamento de descoberta e detecção de pelo menos um ponto de extremidade. DiscoverableServiceHost substitui o método OnOpening de ServiceHost. Se o serviço deve ser detectável, OnOpening chama o método EnableDiscovery.

EnableDiscovery é o coração do DiscoverableServiceHost. Ele cria um host interno de uma classe singleton particular chamado DiscoveryRequestService (consulte do Figura 3).

A Figura 3 da Classe DiscoveryRequestService (parcial)

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
}

O construtor de DiscoveryRequestService aceita os pontos de extremidade de serviço para os quais ele precisa monitorar solicitações de descoberta (esses são basicamente os pontos de extremidade de DiscoverableServiceHost).

EnableDiscovery, em seguida, adiciona para o host de um ponto de extremidade implementando IServiceBusDiscovery, porque DiscoveryRequestService, na verdade, responde às solicitações de descoberta de clientes. O endereço do ponto de extremidade de descoberta padrão para o URI “ DiscoveryRequests ” sob o namespace do serviço. No entanto, você pode alterar esse antes de abrir DiscoverableServiceHost para qualquer outro URI usando a propriedade DiscoveryAddress. Também fechar DiscoverableServiceHost fecha o host para o ponto de extremidade de descoberta.

A Figura 3 relaciona a implementação de DiscoveryRequestService.

OnDiscoveryRequest primeiro cria um proxy para o cliente de descoberta de retorno de chamada. A associação usada é uma simples NetOnewayRelayBinding, mas você pode controlar que definindo a propriedade DiscoveryResponseBinding. Observe que DiscoverableServiceHost tem uma propriedade correspondente para essa finalidade. OnDiscoveryRequest então itera pela coleção de pontos de extremidade fornecidas ao construtor. Para cada ponto de extremidade, ele verifica se o contrato coincide com o contrato solicitado na solicitação de descoberta. Se o contrato for igual, OnDiscoveryRequest procura os escopos associados com o ponto de extremidade e verifica se os escopos coincidem com os escopos opcionais na solicitação de descoberta. Por fim, OnDiscoveryRequest chama de volta ao cliente o endereço, o contrato e o escopo do ponto de extremidade.

Cliente de descoberta

Para o cliente, eu escrevi a classe auxiliar ServiceBusDiscoveryClient, definida como:

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);
}

ServiceBusDiscoveryClient eu modelado DiscoveryClient do WCF e ele tenha usado da mesma forma, conforme mostrado no do Figura 4.

Figura 4 usando 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 é um proxy para o ponto de extremidade IServiceBusDiscovery descoberta de eventos. Os clientes usá-lo para disparar a solicitação de descoberta no serviços localizáveis. O endereço do ponto de extremidade de descoberta padrão é “ DiscoveryRequests ”, mas você pode especificar um endereço diferente usando qualquer um dos construtores de tomar um nome de ponto de extremidade ou um endereço de ponto de extremidade. Ele usará uma instância simples de NetOnewayRelayBinding para o ponto de extremidade de descoberta, mas você pode especificar uma ligação diferente usando qualquer um dos construtores de tomar um nome de ponto de extremidade ou uma instância de ligação. ServiceBusDiscoveryClient oferece suporte à cardinalidade e tempos limite de descoberta, assim como DiscoveryClient.

A Figura 5 mostra a implementação parcial ServiceBusDiscoveryClient.

A Figura 5 de implementação ServiceBusDiscoveryClient (parcial)

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
}

O método Find precisa de uma maneira de receber retornos de chamada de serviços de descoberta. Para esse fim, sempre que for chamado, localizar abre e fecha um host de uma classe singleton sincronizada interno chamado DiscoveryResponseCallback. Localizar adiciona a host de um ponto de extremidade com suporte para IServiceBusDiscoveryCallback. O construtor de DiscoveryResponseCallback aceita um delegado do tipo de ação de < Uri, [] de Uri >. Sempre que um serviço responde novamente, a implementação de DiscoveryResponse invoca esse delegado, fornecendo-o com o endereço de descoberta e o escopo. O método Find usa uma expressão lambda para agregar as respostas em uma instância de FindResponse. Infelizmente, não há nenhum construtor público para FindResponse, assim, localizar usa o método de CreateFindResponse do DiscoveryHelper, que por sua vez usa a reflexão para instanciar a ele. Localizar também cria um identificador de evento waitable. Os sinais de expressão lambda que manipulam quando a cardinalidade é atendida. Depois de chamar DiscoveryRequest, localizar espera que o identificador a ser sinalizado ou a duração da descoberta de expirar e, em seguida, ele anulará o host a parar de processar todas as respostas de descoberta em andamento.

Mais classes do auxiliar do cliente

Embora eu escrevi ServiceBusDiscoveryClient seja funcionalmente idêntico ao DiscoveryClient, beneficiariam de uma experiência de descoberta simplificada oferecida pelo meu ServiceBusDiscoveryHelper:

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>detecta um serviço com uma cardinalidade de uma, DiscoverAddresses <T>detecta todos os pontos de extremidade de serviço disponíveis (cardinalidade de todos) e DiscoverBinding <T>usa o ponto de extremidade do serviço de metadados para descobrir a ligação do ponto de extremidade. Da mesma forma, defini a classe ServiceBusDiscoveryFactory:

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>pressupõe que a cardinalidade de uma, e usa o ponto de extremidade de metadados para obter o endereço e a ligação usada para criar o proxy do serviço. CreateChannels <T>cria proxies para todos os serviços detectados, usando todos os pontos de extremidade de metadados de descoberta.

Anúncios

Para oferecer suporte a anúncios, você pode usar novamente a vinculação de retransmissão de eventos a substituição de difusão seletiva de UDP. Primeiro, defini o contrato de anúncio IServiceBusAnnouncements:

[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);
}

Conforme mostrado no do Figura 6, desta vez, ele é para os clientes para expor um ponto de extremidade de ligação do evento e monitorar os anúncios.

image: Availability Announcements over the Service Bus

Figura 6 do Availability avisos sobre o barramento de serviços

Os serviços irão anunciar sua disponibilidade (pela vinculação unidirecional retransmissão) fornecendo seu endereço (etapa 1 de do Figura 6) e os clientes irão continuar para invocá-los (etapa 2 de do Figura 6).

No lado do serviço de avisos

Meu DiscoveryRequestService oferece suporte a anúncios:

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

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

  // More members 
}

No entanto, em comparação com os anúncios internos do WCF, por padrão ele não anuncia sua disponibilidade. Para permitir que os anúncios, você precisa configurar uma empresa de lançamento com o comportamento de detecção. Na maioria dos casos, isso é tudo o que você terá que fazer. DiscoveryRequestService acionará seus eventos de disponibilidade no URI “ AvailabilityAnnouncements ” sob o namespace do serviço. Você pode alterar esse padrão, definindo a propriedade AnnouncementsAddress antes de abrir o host. Os eventos serão disparados por padrão, usando uma ligação simples retransmissão unidirecional, mas você pode fornecer uma alternativa usando a propriedade AnnouncementsBinding antes de abrir o host. DiscoveryRequestService acionará seus eventos de disponibilidade de forma assíncrona para evitar o bloqueio de operações durante a abertura e fechamento do host. A Figura 7 mostra os elementos de suporte de anúncio de DiscoveryRequestService.

De anúncios com DiscoveryRequestService de suporte, a Figura 7

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); 
    }
  }
}

O método auxiliar de CreateAvailabilityAnnouncementsClient usa uma fábrica de canais para criar um proxy para o ponto de extremidade IServiceBusAnnouncements anúncios de eventos. Após a abertura e antes de fechar DiscoveryRequestService é acionado as notificações. DiscoveryRequestService substitui os OnOpened OnClosed métodos e de ServiceHost. Se o host estiver configurado para anunciar, OnOpened e OnClosed chamar CreateAvailabilityAnnouncementsClient para criar um proxy e passá-lo para o método PublishAvailabilityEvent para acionar o evento de forma assíncrona. Como o ato de acionamento do evento é idêntico para os anúncios de saudação e bye e a única diferença é o método de IServiceBusAnnouncements chamar, PublishAvailabilityEvent aceita um delegado para o método de destino. Para cada ponto de extremidade de DiscoveryRequestService PublishAvailabilityEvent procura os escopos associados a esse ponto de extremidade e filas de lançamento para o pool de segmentos do Microsoft .NET Framework usando um método anônimo de WaitCallback. O método anônimo invoca o delegado fornecido e, em seguida, fecha o proxy de destino subjacente.

Recebendo avisos

Foi que eu tenha as AnnouncementService fornecido pelo WCF, conforme descrito em meu artigo de janeiro, mas há uma longa lista de coisas que eu tenha aprimorado com meu AnnouncementSink <T> e eu não vejo um caso em que você preferir usar AnnouncementService em favor de AnnouncementSink <T>. Eu também queria aproveitar e reutilizar o comportamento de AnnouncementSink <T>e sua classe base.

Portanto, para o cliente, escreveu ServiceBusAnnouncementSink <T> definido como:

[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;}
}

Os construtores de ServiceBusAnnouncementSink <T>exigem que o espaço para nome do serviço.

ServiceBusAnnouncementSink <T>suporta IServiceBusAnnouncements como um singleton de hospedagem interna. ServiceBusAnnouncementSink <T>também publica propriamente dito no registro do barramento de serviço. ServiceBusAnnouncementSink <T>assina os anúncios de disponibilidade no URI “ AvailabilityAnnouncements ” sob o namespace do serviço por padrão. Você pode alterar que (antes de abri-lo), definindo a propriedade AnnouncementsAddress. ServiceBusAnnouncementSink <T>usa um NetEventRelayBinding simples (por padrão) para receber as notificações, mas você pode alterar isso, definindo o AnnouncementsBinding antes de abrir ServiceBusAnnouncementSink <T>. Os clientes de ServiceBusAnnouncementSink <T>pode se inscrever para os representantes de AnnouncementSink <T>para receber os anúncios ou eles podem acessar apenas o endereço no recipiente do endereço base. Por exemplo, consulte o do Figura 8.

A Figura 8 de recebimento de anúncios

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();
   }
   ...
}

A Figura 9 mostra a implementação parcial de ServiceBusAnnouncementSink <T>sem algumas de tratamento de erros.

A Figura 9 Implementando ServiceBusAnnouncementSink <T>(Parcial)

[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
}

O construtor de ServiceBusAnnouncementSink <T>hospeda a mesmo como um singleton e salva o espaço para nome do serviço. Quando você abre ServiceBusAnnouncementSink <T>, ele adiciona ao seu próprio host um ponto de extremidade com suporte para IServiceBusAnnouncements. A implementação do evento de manipulação de métodos de IServiceBusAnnouncements cria uma instância de AnnouncementEventArgs, preenchendo-o com o endereço do serviço anunciado, contrato e escopos e em seguida, chama a implementação da classe base dos métodos respectivos comunicado, como se ele foi chamado usando descoberta do WCF 
regular. Isso preenche a classe base do AddressesContainer <T>e aciona os eventos apropriados de AnnouncementSink <T>. Observe que, para criar uma instância de AnnouncementEventArgs, você deve usar reflexão devido à falta de um construtor público.

O Gerenciador de metadados

Usando o meu suporte para a descoberta de barramento de serviço, estendido o recurso de descoberta da ferramenta Explorer os metadados (apresentado em artigos anteriores) para oferecer suporte ao barramento de serviço. Se você clicar na descobrir botão (consulte do Figura 10), para cada espaço para nome do serviço já ter fornecido as credenciais para, o Gerenciador de metadados irá tentar descobrir metadados trocar os pontos de extremidade dos serviços detectáveis e exibir os pontos de extremidade detectados na árvore.

image: Configuring Discovery over the Service Bus

A Figura 10 de configuração de descoberta de barramento de serviços

O Gerenciador de metadados padrão usando o URI “ DiscoveryRequests ” sob o namespace do serviço. Você pode alterar esse caminho, selecionando barramento de serviços a partir do menu, em seguida, a descoberta, para abrir a caixa de diálogo Configurar AppFabric Service Bus Discovery (consulte do Figura 10).

Para cada espaço para nome de serviço de interesse, a caixa de diálogo permite que você configure o caminho relativo do ponto de extremidade de eventos de descoberta desejado na caixa de texto de descoberta de caminho.

O Gerenciador de metadados também oferece suporte a anúncios de pontos de extremidade da troca de metadados do serviço barramento. Para ativar o recebimento da notificação de disponibilidade, exibir a caixa de diálogo de configuração de descoberta e marque a caixa de seleção Ativar em anúncios de disponibilidade. O Gerenciador de metadados padrão será usando o URI “ AvailabilityAnnouncements ” sob o namespace do serviço especificado, mas podem ser configuradas para cada espaço para nome do serviço de qualquer caminho desejado para o ponto de extremidade de anúncios.

O suporte no Gerenciador de metadados para anúncios torna um barramento de serviço simples e útil, prática ferramenta de monitoramento.

Juval Lowy  é arquiteto de software da IDesign .net e fornece treinamento e consultoria de arquitetura e. Este artigo contém excertos de seu recente livro, “ Programming WCF Services 3ª edição ” (2010 O’Reilly). Ele também é o diretor regional da Microsoft no Vale do Silício. Entre em contato com Lowy em idesign.net de .

Meus agradecimentos aos seguinte especialista técnico para revisar este artigo: Wade Wegner