Il presente articolo è stato tradotto automaticamente.

Cloud computing

Sincronizzazione di più nodi in Windows Azure

Josh Twist

Scaricare il codice di esempio

La cloud rappresenta un cambiamento importante tecnologia e molti esperti del settore prevedere questa modifica è una scala vediamo ogni 12 anni o scopo. Questo livello di entusiasmo è non deve sorprendere se si considera i numerosi benefici che della cloud promette: notevolmente ridotto con costi, elevata disponibilità e scalabilità praticamente infinito, nome, ma alcuni.

Naturalmente, tale spostamento presenta inoltre nel settore con numerose sfide, non meno quelli affrontati dagli sviluppatori di oggi. Ad esempio, come è creare sistemi posizionati in modo ottimale per sfruttare le caratteristiche univoche di cloud?

Fortunatamente, Microsoft nel febbraio ha lanciato Windows Azure Platform, che contiene un numero di pezzi di dimensioni a destra per la creazione di applicazioni che può supportare un numero enorme di utenti pur restando altamente disponibili. Tuttavia, per qualsiasi applicazione ottenere le potenzialità durante la distribuzione della cloud il onus è negli sviluppatori del sistema per sfruttare i vantaggi di ciò che è probabilmente funzionalità più grande dell'area: elasticity.

Elasticity è una proprietà di nuvola piattaforme che consente alle risorse aggiuntive (elaborazione potenza, archiviazione e così via) per essere provisioning su richiesta, fornendo la possibilità di aggiungere altri server della Web farm in pochi minuti, non mesi. Altrettanto importante è la possibilità di rimuovere queste risorse appena più rapidamente.

Un principio chiave della cloud computing è il modello aziendale pay-as-you-go, dove si paga solo per si utilizza. Con Windows Azure, solo pagare per tempo un nodo (Web o ruolo di lavoro eseguito in una macchina virtuale) è distribuito, riducendo il numero di nodi quando non sono necessarie o durante i periodi di quieter dell'azienda, causando una riduzione dei costi diretti.

Pertanto, è molto importante che gli sviluppatori di creare sistemi elastici che reagiscano automaticamente per la fornitura di hardware aggiuntivo, con input minimo o configurazione richiesta dagli amministratori di sistemi.

Scenario 1: Creazione di numeri di ordine

Recentemente, era abbastanza fortunato a lavorare su una prova del concetto esaminato lo spostamento di un'infrastruttura di applicazione Web esistente nella nuvola utilizzando Windows Azure.

Data la natura dei dati dell'applicazione partizionata, era un candidato per Windows Azure tabella archivi principale. Il meccanismo di archiviazione semplice ma con prestazioni elevate, con il supporto per scalabilità praticamente infinito, è stata scelta ideale, con solo uno svantaggio notevole relativi identificatori univoci.

L'applicazione di destinazione consentiti ai clienti di effettuare un ordine e recuperare il relativo numero di ordine. Utilizzando SQL Server o SQL Azure, sarebbe già stato semplice generare un semplice identificatore univoco numerico, ma Windows Azure tabella di archiviazione non offre le chiavi primarie con incremento automatico. Utilizzando Windows Azure tabella archivi 
developers potrebbe invece creare un GUID e utilizzarla come “ chiave ” nella tabella:

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

Il problema utilizzando i GUID è che sia difficile per gli utenti a lavorare. Si supponga di dover leggere il numero di ordine GUID out a un operatore tramite telefono, o prendere nota di tale nel diario. Naturalmente, GUID devono essere univoco in ogni contesto contemporaneamente, in modo che siano abbastanza complessi. Il numero dell'ordine, invece, solo deve essere univoco nella tabella Ordini.

Creazione di un ID univoco semplice in Windows Azure

Un numero relativamente semplici soluzioni al problema del GUID sono stato considerato:

  1. Utilizzare Azure SQL per generare ID univoci: Per diversi motivi, il modello di prova aveva già scontati Azure SQL in favore di Windows Azure tabella Storage, principalmente per la necessità di sistema scalare molti nodi, ognuno con molti thread in esecuzione con i dati.
  2. Utilizzare archiviazione Blob per gestire un valore incrementale: Memorizzare un contatore centrale in Windows Azure Blob Storage. Nodi è in grado di leggere e aggiornare il numero dell'ordine, che fornisce un meccanismo di generazione numero ordine sequenziale semplice da utilizzare per più nodi. Tuttavia, a questo punto la contesa per un sistema occupato che richiede molti nuovi numeri di ordine secondo sarebbe probabilmente ostacolano la scalabilità del sistema.
  3. Partizione ID univoci in ciascun nodo: Creare un contatore di memoria leggero che genera numeri univoci. Per garantire l'univocità tra tutti i nodi, ciascun nodo sarebbe allocato un intervallo di numeri d'ordine, come illustrato in di Figura 1.

Figura 1 un intervallo per l'ordine di allocazione numeri per ciascun nodo per assicurarsi di ID univoci

Nodo Range
C 0-1,000,000
B 1,000,001-2,000,000

Tuttavia, questo approccio genera una serie di domande. Cosa accade quando un nodo esaurisce un intervallo? Cosa succede quando centinaia di nodi viene aggiunti al sistema contemporaneamente? Cosa succede se un nodo si blocca e viene sostituito da un nuovo nodo al runtime Windows Azure? Gli amministratori necessario monitorare questi intervalli strettamente e attenzione per assicurarsi che la configurazione sia corretta oppure affrontare il danneggiamento dei dati.

Invece, era necessario un approccio molto più elegante, una soluzione non necessaria alcuna configurazione a nodi dimostrato contesa minima e garantita l'univocità sempre. A questo scopo, ho creato una combinazione delle opzioni seconda e terza.

Il concetto è relativamente semplice: utilizzare un file di testo di piccole dimensioni in archiviazione blob per memorizzare il numero dell'ultimo ordine. Quando è necessario un nuovo numero di ordine, un nodo può accedere il blob, incrementare il valore e scrivere nuovamente all'archiviazione. Naturalmente, è ragionevole possibilità di un altro nodo sarà necessario accedere blob con lo stesso scopo durante questo processo di lettura-scrittura incremento. Senza un tipo di gestione della concorrenza, i numeri di ordine non sono univoci e i dati potrebbero essere danneggiati. In genere è possibile sono considerati creando un meccanismo di blocco che impedisce il funzionamento con il blob simultanea di più nodi. Tuttavia, i blocchi sono costosi e se il throughput e scalabilità enormi sono Guida temi per l'implementazione, si tratta di essere evitato.

Invece, un approccio la concorrenza ottimistica è favorevole. Con la concorrenza ottimistica, è possibile consentire più attori interagire con una risorsa. Quando la risorsa viene recuperata da un attore, l'attore viene emesso anche un token che indica la versione della risorsa. Quando si verifica un aggiornamento, il token può essere incluso per indicare quale versione della risorsa da modificare. Se la risorsa è già stata modificata da un altro attore, quindi l'aggiornamento avrà esito negativo e l'attore originale può recuperare la versione più recente e riprovare a eseguire l'aggiornamento. Concorrenza ottimistica funziona bene purché la possibilità di conflitti tra gli aggiornamenti sono insufficienti. Viene evitato il costo e la complessità di un blocco e la risorsa è protetta dai danni.

Si supponga che durante le ore di punta, il sistema genera circa 100 nuovi numeri di ordine al secondo. Ciò significherebbe 100 richieste per aggiornare il blob secondo, causando una probabilità estremamente elevata di contesa, significherebbe molti tentativi exacerbating la situazione. Di conseguenza, per ridurre la possibilità che questo si verifichi, ho deciso di allocare i numeri degli ordini in intervalli.

È stata creata una classe denominata il UniqueIdGenerator per incapsulare il problema. La classe rimuove un intervallo di numeri d'ordine dall'archiviazione blob incrementando il valore in porzioni configurabile. Se ogni UniqueIdGenerator impegnare numeri di ordine 1.000 alla volta, il blob dovrebbe essere aggiornato solo in Media ogni 10 secondi, riducendo in modo significativo le possibilità di conflitto. Ogni UniqueIdGenerator è gratuita emettere l'ordine di 1.000 riservata verranno numeri a certi che nessuna istanza della classe che puntano alla stessa risorsa blob rilascerà lo stesso numero di ordine.

Per rendere questo nuovo componente verificabile, è stata specificata un'interfaccia denominata IOptimisticSyncStore che de-coupled la UniqueIdGenerator dal meccanismo di archiviazione. Questo è un ulteriore vantaggio: In futuro, il componente potrebbe utilizzare un diverso tipo di archiviazione dove appropriato. Ecco l'interfaccia:

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

Come si può vedere, è molto semplice interfaccia con solo due metodi: uno per recuperare i dati e l'altro per aggiornarlo, il secondo restituendo un valore booleano in cui false indica che si è verificato un errore di concorrenza ottimistica e il processo deve essere ripetuto.

Un'implementazione di IOptimisticSyncStore che utilizza blob di archiviazione è disponibile nel download del codice (ulteriori informazioni alla fine dell'articolo). Per la maggior parte dell'implementazione è semplice;Tuttavia, vale la pena osservare TryOptimisticWrite è stato implementato il metodo in modo più dettagliato per comprendere come la concorrenza ottimistica.

È semplice utilizzare la concorrenza ottimistica durante l'aggiornamento delle risorse di archiviazione di Windows Azure Blob, grazie a precondizioni ed Entity tag (ETags). Una precondizione è un'istruzione che dichiara uno sviluppatore deve essere true per una richiesta HTTP abbia esito positivo. Se il server Web restituisce l'istruzione su false, deve rispondere con un 412 codice di stato HTTP: “ Precondizione non riuscita. ” ETags fanno inoltre parte della specifica HTTP e identificare una determinata versione di una risorsa, ad esempio un blob. Se il blob viene modificato, l'ETag dovrebbe cambiare anche come illustrato di seguito:

try
{?
  _blobReference.UploadText(?
    data,?
    Encoding.Default,?
    new BlobRequestOptions { ?
    AccessCondition = AccessCondition.IfMatch(
    _blobReference.Properties.ETag) });?
}

Per specificare una precondizione nel codice, è possibile utilizzare il tipo BlobRequestOptions e impostare la proprietà AccessCondition. Se non viene soddisfatta questa condizione di accesso (ad esempio, se un altro nodo aggiornato il blob in breve tempo poiché è stato recuperato), non corrisponde al ETags e dovrebbe essere generata una StorageClientException:

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

L'implementazione controlla l'eccezione per il codice di stato PreconditionFailed e restituisce false in questa istanza. Qualsiasi altro tipo di eccezione è un errore grave e verrà generata di nuovo per la gestione e la registrazione ulteriormente in. Nessuna eccezione significa che l'aggiornamento ha avuto luogo e il metodo restituisce true. L'elenco completo per la classe UniqueIdGenerator viene visualizzato in di Figura 2.

Figura 2 della Classe UniqueIdGenerator completo

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

Il costruttore accetta tre parametri. Il primo è un'implementazione di IOptimisticSyncStore, quali la nostra BlobOptimisticSyncStore discusso in precedenza. Il secondo parametro è rangeSize, un valore integer che indica la dimensione deve essere l'intervallo di numeri allocati dal blob. La più grande in questo intervallo, meno possibilità di conflitti. Invece, più numeri verranno persi se questo nodo dovesse bloccarsi. Il parametro finale maxRetries, un valore integer che indica quante volte il generatore deve tentare di aggiornare il blob in caso di un errore di concorrenza ottimistica. Oltre a questo punto, viene generata un'eccezione.

Metodo NextId è il membro della classe UniqueIdGenerator solo pubblico e viene utilizzato per recuperare il successivo numero univoco. Il corpo del metodo viene sincronizzato per garantire che tutte le istanze della classe sono thread-safe e potrebbero, ad esempio, essere condiviso da tutti i thread in esecuzione l'applicazione Web. Un se istruzione consente di verificare se il generatore ha raggiunto il limite superiore della relativa allocazione di intervallo e, in caso affermativo, chiama UpdateFromSyncStore per recuperare un nuovo intervallo di archiviazione blob.

Metodo UpdateFromSyncStore è la parte più interessante ma finale della classe. L'implementazione di IOptimisticSyncStore viene utilizzato per recuperare il valore massimo dell'allocazione precedente emesso. Il valore viene incrementato di dimensioni dell'intervallo del generatore e questo viene scritto nuovamente all'archiviazione. Un semplice ciclo “ while ” racchiude il corpo per assicurarsi che il numero appropriato di tentativi ha luogo se TryOptimisticWrite restituisce false.

Nel frammento di codice seguente viene illustrato un UniqueIdGenerator da costruire, utilizzando un BlobOptimisticSyncStore con un file denominato “ ordernumber.dat ” in un contenitore denominato “ uniqueids ” (Nota: contenitori di archiviazione blob devono avere nomi minuscoli):

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

Questa istanza Rimuove 1.000 ID dall'intervallo centrale e verrà ripetuta 10 volte in caso di un errore di concorrenza ottimistica prima che venga generata un'eccezione.

Utilizzo di UniqueIdGenerator è ancora più semplice. Dove è necessario un nuovo numero ordine univoco, è sufficiente chiamare NextId:

Int64 orderId = generator.NextId();

Il codice di esempio viene illustrato un ruolo di lavoro Azure Windows che utilizza più thread per assegnare numeri univoci rapidamente e scriverli in un database SQL. L'utilizzo di SQL in questa istanza è semplicemente quello di dimostrare che ogni numero sia univoco, ovvero qualsiasi violazione di questo potrebbe causare una violazione di chiave primaria e genera un'eccezione.

Il vantaggio di questo approccio, ovvero diverso da creare il blob e impostandone il valore su 0 molto all'inizio del ciclo di vita dell'applicazione, è che nessun impegno è necessario per l'amministratore di sistema. Il UniqueIdGenerator attentamente gestisce l'allocazione degli ID a seconda delle impostazioni, ripristinato correttamente in caso di errore e adatta facilmente anche in ambienti più elastico.

Scenario 2: Rilasciare i Hounds E!

Un altro requisito interessante provocato dall'applicazione era la necessità di elaborare rapidamente grandi quantità di dati dopo un evento specifico che si verificherebbe un momento circa noti. A causa della natura dell'elaborazione, Impossibile iniziare il lavoro su uno qualsiasi dei dati dopo questo evento.

Ruoli di lavoro sono una scelta ovvia in questo scenario e sarebbe stato possibile chiedere semplicemente Azure Windows per fornire il numero di ruoli di lavoro necessarie in risposta a eventi citati in precedenza. Tuttavia, nuovi ruoli di provisioning può richiedere fino a 30 minuti e velocità era l'essenza di questo scenario. Pertanto, è stato deciso che dovrebbe essere hydrated i ruoli in anticipo, ma in uno stato sospeso fino a quando rilasciato da un amministratore, ho chiamato “ Release di Hounds E ”! Sono stati considerati due possibili approcci e verrà esaminare ogni volta.

Si noti che, poiché Windows azzurro lavoro ruoli vengono addebitati in base al tempo che sta distribuiti (non in modalità attivamente utilizzano la CPU), questo approccio potrebbe costo maggiore rispetto a semplicemente creando ruoli lavoro in risposta all'evento. Tuttavia il cliente era chiaro che questo investimento è utile per garantire che l'elaborazione potrebbe iniziare più rapidamente possibile.

Approccio I: Polling

Il primo approccio illustrato in Figura 3 aveva ciascun nodo un flag di stato centrale il polling a intervalli regolari (nuovamente, memorizzati in un blob Windows Azure) per determinare se il lavoro potrebbe ancora avviata.

image: Nodes Polling a Central Status Flag

Figura 3 nodi polling un flag di stato centrale

Per sospendere un nodi, un'applicazione client semplicemente era necessario impostare questo flag su true e con il polling successivo dovrebbe essere rilasciato ogni nodo. Lo svantaggio principale di questo approccio è la latenza, potenzialmente grande come l'intervallo di polling. D'altra parte, si tratta di un meccanismo molto semplice e affidabile per implementare.

Questa struttura è illustrata dalla classe PollingRelease disponibile nel codice di esempio. Per supportare testabilità, il meccanismo di archiviazione flag è astratto dietro un'interfaccia in modo analogo per quanto riguarda la classe UniqueIdGenerator. L'interfaccia IGlobalFlag e la relativa implementazione per l'archiviazione di blob vengono visualizzati in Figura 4.

Figura 4 l'interfaccia IGlobalFlag e l'implementazione di archiviazione 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;
    }
  }
}

Si noti che in questo esempio l'esistenza di un file nell'archiviazione blob semplice indica true, indipendentemente da quale il contenuto.

La stessa classe PollingRelease è semplice, come illustrato nella Figura 5 con solo un metodo pubblico denominato Wait.

Figura 5 La 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);
    }
  }
}

Questo metodo blocca qualsiasi chiamante come implementazione IGlobalFlag indica che lo stato è false. Nel frammento di codice seguente viene illustrata la classe PollingRelease in uso:

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

Viene creata un'istanza BlobGlobalFlag punta a un contenitore denominato “ globalflags. ” La classe PollingRelease verrà polling ogni 2,5 secondi la presenza di un file denominato “ inizio ordine processing.dat ”;tutte le chiamate al metodo Wait verranno bloccate fino a quando il file esiste già.

Approccio II: In attesa

Il secondo metodo utilizza il bus di servizi Windows Azure AppFabric per comunicare direttamente con tutti i ruoli di lavoro contemporaneamente e rilasciarli (vedere di Figura 6).

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

Figura 6 tramite il bus azzurro AppFabric servizio Windows per comunicare contemporaneamente con tutti i ruoli di lavoro

Il Bus Service è un servizio di messaggistica e di connettività su larga scala, anche basato su Windows Azure. Facilita la comunicazione tra diversi componenti di un'applicazione distribuita. Service Bus consente di connettere due applicazioni che sarebbero altrimenti difficile comunicare, dovuto alla loro posizione dietro un confine di rete indirizzo translation (NAT) o un indirizzo IP cambiano di frequente, ad esempio in modo ideale. Esula dall'ambito di questo articolo per ottenere una panoramica dettagliata su bus di servizi di Windows Azure AppFabric, ma un'ottima esercitazione è disponibile su MSDN all'indirizzo msdn.microsoft.com/library/ee706736 .

Per illustrare questo approccio, è stata creata una classe denominata ListeningRelease che, come PollingRelease, dispone di un metodo pubblico denominato Wait. Questo metodo si connette al Service Bus e utilizza un ManualResetEvent per bloccare il thread fino a quando non viene ricevuto un segnale:

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

Il metodo ConnectToServiceBus completo è elencato in Figura 7. Utilizza i tipi dall'assembly System.ServiceModel e Microsoft.ServiceBus per esporre una classe denominata UnleashService cloud tramite Windows Azure AppFabric Service Bus, illustrato in Figura 8.

Figura 7 Il metodo 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 la classe 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;
}

Il UnleashService è ospitato da Windows Communication Foundation (WCF) come istanza singola e implementa il contratto IUnleashService, che ha un solo metodo: Libera. ListeningRelease attende una chiamata di questo metodo mediante l'evento Unleashed illustrata in precedenza. Quando la classe ListeningRelease osserva questo evento, viene impostato ManualResetEvent che blocca le chiamate in attesa e vengono rilasciati tutti i thread bloccati.

Nella configurazione del servizio utilizzato NetEventRelayBinding, che supporta il multicasting tramite il bus di servizi, consentendo un numero qualsiasi di editori e sottoscrittori di comunicare attraverso un singolo endpoint. La natura di questa comunicazione broadcast, è necessario che tutte le operazioni sono unidirezionali, come dimostrato dall'interfaccia IUnleashContract:

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

L'endpoint è protetto mediante un segreto condiviso (nome utente e una password complessa). Questi dettagli, qualsiasi client con accesso a Internet può richiamare il metodo Unleash, inclusi ad esempio Administrator Console fornita nell'esempio (vedere di Figura 9).

image: The Administrator Console

Figura 9 della console di amministrazione

Sebbene l'approccio ListeningRelease non immediatamente con latenza inerente nella classe PollingRelease, vi è sempre certa latenza da affrontare. Lo svantaggio principale con l'approccio di ascolto è tuttavia la natura priva di stato nodi dopo la trasmissione del segnale di rilascio il provisioning non sarebbe vedere questo evento e rimarrebbero in sospesi. Ovviamente, una soluzione più ovvia è possibile combinare il bus di servizio e un flag globale nell'archiviazione blob, ma lasciare che come un esercizio per il lettore.

Codice di esempio

La soluzione di esempio associato è disponibile all'indirizzo code.msdn.microsoft.com/mag201011Sync e include un file Leggimi sono elencati i prerequisiti che include le istruzioni di installazione e configurazione. L'esempio utilizza ListeningRelease e PollingRelease UniqueIdGenerator in un unico ruolo di lavoro.

Josh Twist è un responsabile di sviluppo principale dell'applicazione con supporto Premier per team di sviluppatori in Italia e reperibile blog in thejoyofcode.com .

Grazie ai seguenti esperti tecnici per la revisione di questo articolo: David Goon, Morgan Skinnere di Wade Wegner