Este artigo foi traduzido por máquina.

Computação em nuvem

Sincronizando vários nós no Windows Azure

Josh Twist

Baixe o código de exemplo

A nuvem representa uma mudança importante tecnologia e muitos especialistas do setor prevêem essa alteração é de uma escala que vemos que somente a cada 12 anos ou menos. Esse nível de entusiasmo é surpreendente dificilmente ao considerar os diversos benefícios que promete a nuvem: reduzido significativamente executando os custos, alta disponibilidade e escalabilidade quase infinita, nome, mas alguns.

É claro que, como uma mudança também apresenta o setor com vários desafios, não menos importante aqueles enfrentados pelos desenvolvedores de hoje. Por exemplo, como criamos sistemas que são posicionados de maneira ideal para tirar proveito dos recursos exclusivos da nuvem?

Felizmente, a Microsoft de fevereiro iniciada a plataforma Windows Azure, que contém um número de partes com o tamanho ideal para oferecer suporte a criação de aplicativos que oferecem suporte a grandes números de usuários enquanto permanece altamente disponível. Entretanto, para qualquer aplicativo atingir todo o seu potencial quando implantado na nuvem, a obrigação é que os desenvolvedores do sistema para aproveitar o que é, indiscutivelmente, recurso de maior da nuvem: elasticity.

Elasticity é uma propriedade de plataformas de nuvem que permite que recursos adicionais (poder da computação, armazenamento e assim por diante) a ser provisionada sob demanda, fornecendo a capacidade de adicionar servidores adicionais para o farm da Web em questão de minutos, e não meses. Igualmente importante é a capacidade de remover esses recursos apenas o mais rápido.

Uma filosofia chave da nuvem de computação é o modelo comercial flexível, onde apenas paga para você usar. Com o Windows Azure, você só pagar o tempo de implantação de um nó (uma Web ou a função de trabalho que executam o em uma máquina virtual), reduzindo o número de nós quando não são necessários ou durante os períodos tranqüilo da sua empresa, o que resulta em uma redução de custos diretos.

Portanto, é extremamente importante que os desenvolvedores criar sistemas de Elástico reagem automaticamente para a provisão de hardware adicional, com o mínimo de entrada ou de configuração necessárias dos administradores de sistemas.

Cenário 1: Criar números de pedidos

Recentemente, fui sorte trabalhar em uma prova de conceito observou movendo uma infra-estrutura de aplicativo da Web existente para a nuvem usando Azure do Windows.

Devido à natureza particionada de dados do aplicativo, ele era um candidato ideal para armazenamento do Windows Azure tabela. Esse mecanismo de armazenamento simples, mas alto desempenho — com suporte a quase infinita de escalabilidade — era uma opção ideal, com apenas um inconveniente dignas de nota sobre identificadores exclusivos.

O aplicativo de destino permitido aos clientes um pedido e recuperar o número do pedido. Usando o SQL Server ou SQL Azure, seria tenha sido fácil de gerar um identificador exclusivo, numérico, simples, mas Windows Azure tabela Storage não oferece o incremento de automático chaves primárias. Em vez disso, 
developers usando o armazenamento de tabela de Azure do Windows pode criar um GUID e usá-la como “ chave ” na tabela:

505EAB78-6976-4721-97E4-314C76A8E47E

O problema com o uso de GUIDs é que elas são difíceis para seres humanos trabalhar com. Imagine ter que ler o número de ordem de sua GUID check-out para um operador por telefone — ou anote isso no seu diário. É claro que, GUIDs devem ser exclusivos em cada contexto simultaneamente, para que fiquem bem complexas. O número da ordem, por outro lado, só deve ser exclusivo na tabela Pedidos.

A criação de uma identificação exclusiva da Simple no Windows Azure

Um número de soluções relativamente simples para o problema da GUID foram considerado:

  1. Use SQL Azure para gerar identificações exclusivas: Por várias razões, a verificação do conceito já tinha descontado Azure SQL em favor do armazenamento do Windows Azure tabela — principalmente devido à necessidade do sistema para ser dimensionada para vários nós, cada um com vários segmentos em execução com os dados.
  2. Use o armazenamento de blob para gerenciar um valor de incremento: Armazene um contador central no armazenamento do Windows Azure irregular. Nós podem ler e atualizar o número da ordem, fornecendo um mecanismo de geração de número de ordem seqüencial simples para uso por vários nós. No entanto, o disputa neste momento por um sistema ocupado que exigem muitos novos números de pedidos por segundo provavelmente seria impedem o a escalabilidade do sistema.
  3. As identificações exclusivas em cada nó da partição: Crie um contador de memória leve que gere números de ordem exclusivo. Para garantir a exclusividade em todos os nós, cada nó seria alocado um intervalo de números de ordem, conforme mostrado no do Figura 1.

Figura 1 de alocação de um pedido de intervalo de números para cada nó para garantir que as identificações exclusivas

Intervalo
A 0-1,000,000
B 1,000,001-2,000,000

No entanto, essa abordagem gera um número de perguntas. O que acontece quando um nó esgotar um intervalo? O que acontece quando centenas de nós são adicionadas ao sistema de uma só vez? Se um nó falha e é substituído pelo tempo de execução do Windows Azure com um nó atualizado? Os administradores precisaria intimamente monitorar esses intervalos e tome cuidado para garantir que a configuração está correta ou enfrentar corrupção de dados.

Em vez disso, uma abordagem muito mais elegante era necessário — uma solução que é não necessária nenhuma configuração por nó, demonstrado pouco contenção e a garantia de exclusividade em todos os momentos. Para conseguir isso, criei um híbrido das opções a segunda e terceira.

O conceito era relativamente simples: Use um pequeno arquivo de texto em um blob de armazenamento para armazenar o último número de ordem. Quando um novo número de ordem for necessário, um nó pode acessar esse blob, incremente o valor e gravá-lo de volta para o armazenamento. É claro, há uma chance razoável de que outro nó será acessaram o blob com a intenção mesmo durante o processo de incremento de leitura-gravação. Sem algum tipo de gerenciamento de simultaneidade, os números de pedidos não seria exclusivos e os dados seriam corrompidos. Tradicionalmente, eu poderia considerou criando um mecanismo de bloqueio que impede que os vários nós trabalhando com o blob simultaneamente. No entanto, os bloqueios são caros e se a taxa de transferência e a escalabilidade de massa estão orientando temas para a implementação, eles são para ser evitada.

Em vez disso, uma abordagem usando simultaneidade otimista é favorável. Com concorrência otimista, permitimos que vários atores interagir com um recurso. Quando o recurso é recuperado por um ator, o ator também recebe um símbolo que indica a versão do recurso. Quando ocorre uma atualização, o token pode ser incluído para indicar qual versão do recurso está sendo modificado. Se o recurso já foi modificado por outro ator, a atualização falhará e o ator original pode recuperar a versão mais recente e tentar a atualização novamente. Simultaneidade otimista funciona bem contanto que as chances de contenção de atualizações são baixas. O custo e complexidade de um bloqueio é evitado e o recurso está protegido contra corrupção.

Imagine que, durante os horários de pico, o sistema emite cerca de 100 novos números de pedidos por segundo. Isso significaria 100 solicitações para atualizar o blob de cada segundo, causando uma chance extremamente alta de disputa, que significa que tentativas de muitos, exacerbating a situação. Portanto, para reduzir a possibilidade deste ocorrer, eu decidi alocar números de pedidos nos intervalos.

Uma classe chamada a UniqueIdGenerator foi criada para encapsular esse comportamento. A classe elimina um intervalo de números de pedidos do armazenamento de blob, incrementar o valor em blocos configuráveis. Se cada UniqueIdGenerator reservar mil números de ordens ao mesmo tempo, o blob seria apenas atualizado em média a cada 10 segundos, reduzindo significativamente a possibilidade de contenção. Cada UniqueIdGenerator é livre para emitir sua ordem de 1000 reservado irão inserir números na certeza de que nenhuma outra instância da classe apontando para o mesmo recurso blob irá emitir o mesmo número de ordem.

Para fazer com que esse novo componente testável, foi especificada uma interface denominada IOptimisticSyncStore que separado do UniqueIdGenerator do mecanismo de armazenamento específico. Isso tem uma vantagem adicional: No futuro, o componente poderia usar um tipo diferente de armazenamento onde for apropriado. Esta é a interface:

public interface IOptimisticSyncStore
{
  string GetData();
  bool TryOptimisticWrite(string data);
}

Como você pode ver, é uma interface simples com apenas dois métodos: um para recuperar os dados e outra para atualizá-lo, o último retornando um valor booleano onde false indica que houve uma falha de simultaneidade otimista e o processo deve ser repetido.

Uma implementação de IOptimisticSyncStore que usa blob de armazenamento está disponível no download de código (detalhes do final do artigo). Na maior parte, a implementação é simples;No entanto, vale a pena observar TryOptimisticWrite implementou o método mais detalhadamente a entender a simultaneidade otimista como.

É fácil de usar a simultaneidade otimista ao atualizar recursos do Windows Azure irregular Storage, obrigado a pré-condições e o Entity Tags (ETags). Um pré-condição é uma instrução que declara um desenvolvedor deve ser verdadeira para uma solicitação HTTP ser bem-sucedida. Se o servidor Web avaliará a instrução para false, ele deve responder com um 412 de código de status HTTP: “ Falha na pré-condição ”. ETags também fazem parte da especificação do HTTP e identificar uma determinada versão de um recurso, como, por exemplo, um blob. Se o blob é alterado, o ETag deve também serão alteradas, conforme mostrado aqui:

try
{

  _blobReference.UploadText(

    data,

    Encoding.Default,

    new BlobRequestOptions { 

    AccessCondition = AccessCondition.IfMatch(
    _blobReference.Properties.ETag) });

}

Para especificar uma pré-condição no código, usamos o tipo BlobRequestOptions e defina a propriedade AccessCondition. Se esta condição de acesso não esteja satisfeita (por exemplo, se o outro nó atualizado o blob em um curto período de tempo desde que foi recuperada), o ETags não coincidem e um StorageClientException deve ser lançada:

catch (StorageClientException exc)
{
  if (exc.StatusCode == HttpStatusCode.PreconditionFailed)
  {
    return false;
  }
  else
  {
    throw;
  }
}
return true;

A implementação verifica a exceção para o código de status PreconditionFailed e retornará falsa nesta instância. Qualquer outro tipo de exceção é uma falha grave e for relançado para tratamento e o registro adicional no. Nenhuma exceção significa que a atualização foi feita e o método retornará true. Do Figura 2 mostra a listagem completa para a classe UniqueIdGenerator.

Da Classe de UniqueIdGenerator total, a Figura 2

public class UniqueIdGenerator
{ 
    private readonly object _padLock = new object();
    private Int64 _lastId;
    private Int64 _upperLimit;
    private readonly int _rangeSize;
    private readonly int _maxRetries;
    private readonly IOptimisticSyncStore _optimisticSyncStore;

    public UniqueIdGenerator(
      IOptimisticSyncStore optimisticSyncStore,
      int rangeSize = 1000,
      int maxRetries = 25)
    {
      _rangeSize = rangeSize;
      _maxRetries = maxRetries;
      _optimisticSyncStore = optimisticSyncStore;

      UpdateFromSyncStore();
    }

    public Int64 NextId()
    {
      lock (_padLock)
      {
        if (_lastId == _upperLimit)
        {
          UpdateFromSyncStore();
        }
        return _lastId++;
      }
    }

    private void UpdateFromSyncStore()
    {
      int retryCount = 0;
      // maxRetries + 1 because the first run isn't a 're'try.
while (retryCount < _maxRetries + 1)
      {
        string data = _optimisticSyncStore.GetData();

        if (!Int64.TryParse(data, out _lastId))
        {
          throw new Exception(string.Format(
            "Data '{0}' in storage was corrupt and " +
            "could not be parsed as an Int64", data));
        }

        _upperLimit = _lastId + _rangeSize;

        if (_optimisticSyncStore.TryOptimisticWrite(
          _upperLimit.ToString()))
        {
          return;
        }

        retryCount++;
        // update failed, go back around the loop
      }

      throw new Exception(string.Format(
        "Failed to update the OptimisticSyncStore after {0} attempts",
        retryCount));
    }
}

O construtor utiliza três parâmetros. A primeira é uma implementação de IOptimisticSyncStore, como, por exemplo, nossa BlobOptimisticSyncStore discutidos anteriormente. O segundo parâmetro é rangeSize, um valor inteiro que indica o tamanho do intervalo de números alocados a partir do blob deve ser. Quanto maior esse intervalo, menos chance de contenção. No entanto, mais números serão perdidos se esse nó falhar. O parâmetro final é maxRetries, um valor inteiro que indica o número de vezes que o gerador deve tentar atualizar o blob em caso de uma falha de concorrência otimista. Além desse ponto, uma exceção é gerada.

O método NextId é o membro apenas público da classe UniqueIdGenerator e é usado para buscar o próximo número exclusivo. O corpo do método é sincronizado para garantir que nenhuma instância da classe é thread-safe e poderia, por exemplo, compartilhada entre todos os threads que executam seu aplicativo da Web. Um caso instrução verifica para ver se o gerador atingiu o limite superior de sua alocação de intervalo e, em caso afirmativo, chama UpdateFromSyncStore para buscar um novo intervalo de armazenamento de blob.

O método UpdateFromSyncStore é a parte final, mas é mais interessante da classe. A implementação de IOptimisticSyncStore é usada para buscar o limite superior da alocação anterior emitido. O valor é incrementado por tamanho do intervalo do gerador e isso é gravado de volta para o armazenamento. Um simples loop “ while ” circunscreve o corpo para garantir que o número apropriado de novas tentativas ocorre se TryOptimisticWrite retorna false.

O trecho de código a seguir mostra um UniqueIdGenerator sendo construído, usando um BlobOptimisticSyncStore com um arquivo chamado “ ordernumber.dat ” em um recipiente chamado “ uniqueids ” (Observação: recipientes em um blob de armazenamento devem ter nomes em letras minúsculas):

IOptimisticSyncStore storage = new BlobOptimisticSyncStore(
  CloudStorageAccount.DevelopmentStorageAccount, 
  "uniqueids", 
  "ordernumber.dat");
UniqueIdGenerator
  generator = new UniqueIdGenerator(storage, 1000, 10);

Esta instância remove as identificações de 1000 a faixa central e vai ser repetida 10 vezes em caso de uma falha de concorrência otimista antes de lançar uma exceção.

Usar o UniqueIdGenerator é ainda mais simples. Sempre que precisar de um novo número de ordem exclusivo, simplesmente chame NextId:

Int64 orderId = generator.NextId();

O código de exemplo mostra uma função de trabalho do Windows Azure que usa vários threads para alocar números de ordem exclusivo de forma rápida e gravá-los em um banco de dados SQL. O uso do SQL nesta instância é simplesmente para provar que cada número da ordem é exclusivo — qualquer violação poderia resultar em uma violação de chave primária e lançar uma exceção.

A vantagem dessa abordagem — seja o blob de criação e a configuração de seu valor como 0 muito no início do tempo de vida do aplicativo — é que nenhum esforço é obrigatório do administrador de sistemas. O UniqueIdGenerator cuidadosamente gerencia a alocação de IDs de acordo com as configurações, recupera normalmente no caso de falha e se adapta com facilidade, mesmo em ambientes mais Elástico.

Cenário 2: Lançamento de Hounds!

Outro requisito interessante imposto pelo aplicativo era necessário para processar rapidamente grandes quantidades de dados após um evento especificado que ocorreria em um tempo aproximadamente conhecido. Devido à natureza do processamento, trabalho não foi possível iniciar qualquer um dos dados até que este evento.

Funções de operador são uma opção óbvia nesse cenário e teria sido possível simplesmente solicitar Azure do Windows para fornecer o número necessário de funções de operador em resposta ao evento mencionados anteriormente. No entanto, o provisionamento de novas funções pode levar até 30 minutos e velocidade era de que a essência desse cenário. Portanto, foi decidido que as funções poderiam ser hydrated antecipadamente, mas em um estado pausado até o lançamento por um administrador, eu o chamado “ Release do Hounds ”! Dois possíveis abordagens foram consideradas e examinarei cada vez.

Observe que, porque o Windows Azure funções de operador são cobradas com base no tempo que eles são implantados (não em usarem como ativamente a CPU), essa abordagem seria custo mais comparado ao simplesmente criando funções de operador em resposta ao evento. No entanto, o cliente estava claro que esse investimento era valha a pena para garantir que o processamento poderia começar mais rápido possível.

Abordagem Sondagem

A primeira abordagem mostrada no Figura 3, tinha cada nó pesquisar um sinalizador de estado central em intervalos regulares (novamente, armazenado em um blob de Azure do Windows) para determinar se o trabalho ainda poderia começar.

image: Nodes Polling a Central Status Flag

A Figura 3 de nós de um sinalizador de status da Central de pesquisa

Para pausar un nós de um aplicativo cliente simplesmente tinha que defina esse sinalizador como true e com pesquisa subseqüente, cada nó deve ser liberado. A principal desvantagem dessa abordagem é a latência, possivelmente tão grande quanto o intervalo de sondagem. Por outro lado, isso é um mecanismo bastante simples e confiável para implementar.

Esse design é demonstrado pela classe PollingRelease disponível no código de exemplo. Para oferecer suporte à capacidade de teste, o mecanismo de armazenamento de sinalizador foi abstraído por trás de uma interface da mesma maneira que para a classe UniqueIdGenerator. A interface IGlobalFlag e a implementação fornecida para o armazenamento de blob são mostrados em Figura 4.

Figura 4 da interface IGlobalFlag e implementação para o armazenamento de blob

public interface IGlobalFlag
{
  bool GetFlag();
  void SetFlag(bool status);
}

public class BlobGlobalFlag : IGlobalFlag
{
  private readonly string _token = "Set";
  private readonly CloudBlob _blobReference;
  public BlobGlobalFlag(CloudStorageAccount account, string container,    
    string address)
  {
    var blobClient = account.CreateCloudBlobClient();
    var blobContainer =   
      blobClient.GetContainerReference(container.ToLower());
    _blobReference = blobContainer.GetBlobReference(address);
  }

  public void SetFlag(bool status)
  {
    if (status)
   {
      _blobReference.UploadText(_token);
    }
    else
    {
      _blobReference.DeleteIfExists();
    }
  }

  public bool GetFlag()
  {
    try
    {
      _blobReference.DownloadText();
      return true;
    }
    catch (StorageClientException exc)
    {
      if (exc.StatusCode == System.Net.HttpStatusCode.NotFound)
      {
        return false;
      }
      throw;
    }
  }
}

Observe que neste exemplo, a simples existência de um arquivo no armazenamento de blob indica na verdade, não importa qual o conteúdo.

A própria classe PollingRelease é simples, conforme mostrado no do Figura 5, com apenas um método público chamado espera.

A Figura 5 da Classe PollingRelease

public class PollingRelease 
{
  private readonly IGlobalFlag _globalFlag;
  private readonly int _intervalMilliseconds;

  public PollingRelease(IGlobalFlag globalFlag, 
    int intervalMilliseconds)
  {
    _globalFlag = globalFlag;
    _intervalMilliseconds = intervalMilliseconds;
  }

  public void Wait()
  {
    while (!_globalFlag.GetFlag())
    {
      Thread.Sleep(_intervalMilliseconds);
    }
  }
}

Este método bloqueia qualquer chamador, desde que a implementação de IGlobalFlag indica que o seu status é falso. O trecho de código a seguir mostra a classe PollingRelease em uso:

BlobGlobalFlag globalFlag = new BlobGlobalFlag(
  CloudStorageAccount.DevelopmentStorageAccount,
  "globalflags",
  "start-order-processing.dat");
PollingRelease pollingRelease = new PollingRelease(globalFlag, 2500);
pollingRelease.Wait();

Uma instância de BlobGlobalFlag é criada, apontando para um recipiente chamado “ globalflags ”. A classe PollingRelease irá pesquisar cada 2,5 segundos para a presença de um arquivo chamado “ início-ordem-processing.dat ”;qualquer chamada para o método Wait será bloqueada até que esse arquivo existir.

Abordagem II: Ouvindo

A segunda abordagem usa o barramento de serviços Windows Azure AppFabric para se comunicar diretamente com todas as funções de trabalho e de versão-los (consulte do Figura 6) simultaneamente.

image: Using the Azure Service Bus to Simultaneously Communicate with All Worker Roles

De usando o barramento de serviços de AppFabric Azure do Windows para se comunicar simultaneamente com todas as funções de trabalho, a Figura 6

O barramento de serviços é um em larga escala serviço de mensagens e conectividade, também baseado em Windows Azure. Ele facilita a comunicação segura entre diferentes componentes de um aplicativo distribuído. O barramento de serviços oferece uma maneira ideal para conectar-se dois aplicativos que seriam caso contrário, encontrar dificuldade para se comunicar, devido ao seu local de um limite NAT (conversão) do endereço de rede ou um endereço IP mudam freqüentemente, por exemplo. Isso está além do escopo deste artigo para dar uma visão geral detalhada do barramento de serviços do Windows Azure AppFabric, mas um excelente tutorial está disponível no MSDN em msdn.microsoft.com/library/ee706736 de .

Para demonstrar essa abordagem, foi criada uma classe chamada ListeningRelease que, como PollingRelease, tem um método público chamado Wait. Esse método se conecta ao barramento de serviços e usa um ManualResetEvent para bloquear o thread até que um sinal:

public void Wait()
{
  using (ConnectToServiceBus())
  {
    _manualResetEvent.WaitOne();
  }
}

O método ConnectToServiceBus completo está listado em Figura 7. Ele usa tipos de conjuntos de System. ServiceModel e Microsoft.ServiceBus para expor uma classe chamada UnleashService na nuvem via Windows Azure AppFabric Service Bus, mostrado em Figura 8.

A Figura 7 do Método ConnectToServiceBus

private IDisposable ConnectToServiceBus()
{
  Uri address = ServiceBusEnvironment.CreateServiceUri("sb",  
    _serviceNamespace, _servicePath);
  TransportClientEndpointBehavior sharedSecretServiceBusCredential =  
    new TransportClientEndpointBehavior();
  sharedSecretServiceBusCredential.CredentialType =  
    TransportClientCredentialType.SharedSecret;
  sharedSecretServiceBusCredential.Credentials.SharedSecret.
IssuerName = _issuerName;
  sharedSecretServiceBusCredential.Credentials.SharedSecret.
IssuerSecret = _issuerSecret;

  // Create the single instance service, which raises an event
  // when the signal is received.
UnleashService unleashService = new UnleashService();
  unleashService.Unleashed += new  
    EventHandler(unleashService_Unleashed);

  // Create the service host reading the configuration.
ServiceHost host = new ServiceHost(unleashService, address);

  IEndpointBehavior serviceRegistrySettings = 
    new ServiceRegistrySettings(DiscoveryType.Public);

  foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
  {
    endpoint.Behaviors.Add(serviceRegistrySettings);
    endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
  }

  host.Open();

  return host;
}

Figura 8 A classe de UnleashService

[ServiceBehavior(InstanceContextMode= InstanceContextMode.Single)]
public class UnleashService : IUnleashContract
{
  public void Unleash()
  {
    OnUnleashed();
  }

  protected virtual void OnUnleashed()
  {
    EventHandler temp = Unleashed;
    if (temp != null)
    {
      temp(this, EventArgs.Empty);
    }
  }

  public event EventHandler Unleashed;
}

O UnleashService é hospedado pelo Windows Communication Foundation (WCF) como uma única instância e implementa o contrato IUnleashService, que tem apenas um método: Descubra. ListeningRelease atende a uma invocação desse método por meio do evento Unleashed mostrado anteriormente. Quando a classe ListeningRelease observa esse evento, o que está bloqueando no momento nenhuma chamada para aguardar ManualResetEvent é definido e todos os threads bloqueados são lançados.

Na configuração do serviço, usei NetEventRelayBinding, que oferece suporte à difusão seletiva através de barramento de serviços, permitindo que qualquer número de editores e assinantes para se comunicar por meio de um único ponto de extremidade. A natureza dessa comunicação de difusão exige que todas as operações são unidirecionais, conforme demonstrado pela interface IUnleashContract:

[ServiceContract]
public interface IUnleashContract
{
  [OperationContract(IsOneWay=true)]
  void Unleash();
}

O ponto de extremidade é protegido usando um segredo compartilhado (nome de usuário e senha complexa). Esses detalhes, qualquer cliente de acesso à Internet poderia chamar o método de Unleash — incluindo, por exemplo, o console do administrador fornecido em uma amostra (consulte do Figura 9).

image: The Administrator Console

A Figura 9 do console do administrador

Embora a abordagem ListeningRelease faz imediatamente com a latência inerente na classe PollingRelease, ainda há alguns para lidar com a latência. No entanto, a principal desvantagem da abordagem de escuta é sua natureza sem estado, de modo que todos nós provisionado após o sinal de lançamento foi transmitido não vir esse evento e permanecerão em pausa. É claro que, uma solução óbvia seria combinar o barramento de serviços e um sinalizador global do armazenamento de blob, mas deixarei isso como um exercício para o leitor.

Código de exemplo

A solução de exemplo que acompanha está disponível em code.msdn.microsoft.com/mag201011Sync de e inclui um arquivo Leiame que lista os pré-requisitos e inclui instruções de instalação e configuração. O exemplo usa o ListeningRelease, PollingRelease e UniqueIdGenerator em uma única função de trabalho.

Josh apertada  é gerente de desenvolvimento de aplicativo principal com o Premier Support para a equipe de desenvolvedores no Reino Unido e, visite seu blog de thejoyofcode.com .

Graças aos seguintes especialistas técnicos para revisão deste artigo: David Goon, Morgan Skinnere do Wade Wegner