Condividi tramite


Usare la ridondanza geografica per progettare applicazioni a disponibilità elevata

Le infrastrutture basate sul cloud come Archiviazione di Azure offrono una piattaforma a disponibilità elevata e durevole per l'hosting di dati e applicazioni. Gli sviluppatori di applicazioni basate sul cloud devono considerare attentamente come sfruttare questa piattaforma per massimizzare tali vantaggi per gli utenti. Archiviazione di Azure offre opzioni di ridondanza geografica per garantire la disponibilità elevata anche durante un'interruzione a livello di area. Gli account di archiviazione configurati per la replica con ridondanza geografica vengono replicati in modo sincrono nell'area primaria e replicati in modo asincrono in un'area secondaria a centinaia di chilometri di distanza.

Archiviazione di Azure offre due opzioni per la replica con ridondanza geografica: Archiviazione con ridondanza geografica e archiviazione con ridondanza geografica della zona. Per usare le opzioni di ridondanza geografica di Archiviazione di Azure, assicurarsi che l'account di archiviazione sia configurato per l'archiviazione con ridondanza geografica e accesso in lettura (RA-GRS) o l'archiviazione con ridondanza geografica della zona (RA-GZRS). In caso contrario, è possibile ottenere altre informazioni su come modificare il tipo di replica dell'account di archiviazione.

Questo articolo illustra come progettare un'applicazione che continuerà a funzionare, anche se in una capacità limitata, anche quando si verifica un'interruzione significativa nell'area primaria. Se l'area primaria non è più disponibile, l'applicazione può passare facilmente a eseguire operazioni di lettura nell'area secondaria fino a quando l'area primaria non risponde nuovamente.

Considerazioni sulla progettazione delle applicazioni

È possibile progettare l'applicazione per gestire errori temporanei o interruzioni significative leggendo dall'area secondaria quando si verifica un problema che interferisce con la lettura dall'area primaria. Quando l'area primaria è nuovamente disponibile, l'applicazione può tornare alla lettura dall'area primaria.

Tenere presenti queste considerazioni chiave quando si progetta l'applicazione per la disponibilità e la resilienza usando RA-GRS o l'archiviazione con ridondanza geografica e accesso in lettura:

  • Una copia di sola lettura dei dati archiviati nell'area primaria viene replicata in modo asincrono in un'area secondaria. Questa replica asincrona significa che la copia di sola lettura nell'area secondaria è infine coerente con i dati nell'area primaria. Il servizio di archiviazione determina la posizione dell'area secondaria.

  • È possibile usare le librerie client di Archiviazione di Azure per eseguire richieste di lettura e aggiornamento sull'endpoint dell'area primaria. Se l'area primaria non è disponibile, è possibile reindirizzare automaticamente le richieste di lettura all'area secondaria. È anche possibile configurare l'app per inviare richieste di lettura direttamente all'area secondaria, se necessario, anche quando l'area primaria è disponibile.

  • Se l'area primaria non è più disponibile, è possibile avviare un failover dell'account. Quando si esegue il failover nell'area secondaria, le voci DNS che puntano all'area primaria vengono modificate in modo da puntare all'area secondaria. Al termine del failover, l'accesso in scrittura viene ripristinato per gli account GRS e RA-GRS. Per altre informazioni, vedere Ripristino di emergenza e failover dell'account di archiviazione.

Uso di dati coerenti alla fine

La soluzione proposta presuppone che sia accettabile restituire dati potenzialmente obsoleti all'applicazione chiamante. Poiché i dati nell'area secondaria sono alla fine coerenti, è possibile che l'area primaria diventi inaccessibile prima che un aggiornamento dell'area secondaria abbia terminato la replica.

Si supponga, ad esempio, che il cliente invii correttamente un aggiornamento, ma l'area primaria ha esito negativo prima che l'aggiornamento venga propagato all'area secondaria. Quando il cliente chiede di leggere nuovamente i dati, riceve i dati non aggiornati dall'area secondaria anziché dai dati aggiornati. Quando si progetta l'applicazione, è necessario decidere se questo comportamento è accettabile o meno. In caso affermativo, è anche necessario considerare come inviare una notifica all'utente.

Più avanti in questo articolo verranno fornite altre informazioni sulla gestione dei dati coerenti alla fine e su come controllare la proprietà Ora ultima sincronizzazione per valutare eventuali discrepanze tra i dati nelle aree primarie e secondarie.

Gestione dei servizi separatamente o tutti insieme

Sebbene improbabile, è possibile che un servizio (BLOB, code, tabelle o file) diventi non disponibile mentre gli altri servizi sono ancora completamente funzionanti. È possibile gestire i tentativi per ogni servizio separatamente oppure gestire i tentativi genericamente per tutti i servizi di archiviazione insieme.

Ad esempio, se si usano code e BLOB nell'applicazione, è possibile decidere di inserire codice separato per gestire gli errori ritentabili per ogni servizio. In questo modo, un errore del servizio BLOB influirà solo sulla parte dell'applicazione che gestisce i BLOB, lasciando le code per continuare l'esecuzione come di consueto. Se, tuttavia, si decide di gestire tutti i tentativi del servizio di archiviazione insieme, le richieste ai servizi BLOB e di accodamento saranno interessate se uno dei due servizi restituisce un errore riprovabile.

In definitiva, questa decisione dipende dalla complessità dell'applicazione. È consigliabile gestire gli errori in base al servizio per limitare l'impatto dei tentativi. In alternativa, è possibile decidere di reindirizzare le richieste di lettura per tutti i servizi di archiviazione all'area secondaria quando si rileva un problema con qualsiasi servizio di archiviazione nell'area primaria.

Esecuzione dell'applicazione in modalità di sola lettura

Per prepararsi in modo efficace a un'interruzione nell'area primaria, l'applicazione deve essere in grado di gestire le richieste di lettura non riuscite e le richieste di aggiornamento non riuscite. Se l'area primaria ha esito negativo, le richieste di lettura possono essere reindirizzate all'area secondaria. Tuttavia, le richieste di aggiornamento non possono essere reindirizzate perché i dati replicati nell'area secondaria sono di sola lettura. Per questo motivo, è necessario progettare l'applicazione per poter essere eseguita in modalità di sola lettura.

Ad esempio, è possibile impostare un flag controllato prima che eventuali richieste di aggiornamento vengano inviate ad Archiviazione di Azure. Quando viene inviata una richiesta di aggiornamento, è possibile ignorare la richiesta e restituire una risposta appropriata all'utente. È anche possibile scegliere di disabilitare completamente determinate funzionalità fino a quando il problema non viene risolto e notificare agli utenti che le funzionalità non sono temporaneamente disponibili.

Se si decide di gestire gli errori per ogni servizio separatamente, sarà anche necessario gestire la possibilità di eseguire l'applicazione in modalità di sola lettura per servizio. Ad esempio, è possibile configurare flag di sola lettura per ogni servizio. È quindi possibile abilitare o disabilitare i flag nel codice, in base alle esigenze.

La possibilità di eseguire l'applicazione in modalità di sola lettura offre anche la possibilità di garantire funzionalità limitate durante un aggiornamento principale dell'applicazione. È possibile attivare l'esecuzione dell'applicazione in modalità di sola lettura e puntare al data center secondario, assicurando che nessuno acceda ai dati nell'area primaria durante l'esecuzione degli aggiornamenti.

Gestione degli aggiornamenti durante l'esecuzione in modalità di sola lettura

Esistono molti modi per gestire le richieste di aggiornamento durante l'esecuzione in modalità di sola lettura. Questa sezione è incentrata su alcuni modelli generali da considerare.

  • È possibile rispondere all'utente e notificargli che le richieste di aggiornamento non sono attualmente in corso di elaborazione. Ad esempio, un sistema di gestione dei contatti potrebbe consentire agli utenti di accedere alle informazioni di contatto ma non di apportare aggiornamenti.

  • È possibile accodare gli aggiornamenti in un'altra regione. In questo caso, è necessario scrivere le richieste di aggiornamento in sospeso in una coda in un'area diversa e quindi elaborare tali richieste dopo che il data center primario torna online. In questo scenario, è necessario informare l'utente che la richiesta di aggiornamento è in coda per un'elaborazione successiva.

  • È possibile scrivere gli aggiornamenti in un account di archiviazione in un'altra area. Quando l'area primaria torna online, è possibile unire tali aggiornamenti nei dati primari, a seconda della struttura dei dati. Ad esempio, se si creano file separati con un indicatore di data/ora nel nome, è possibile copiare nuovamente tali file nell'area primaria. Questa soluzione può essere applicata ai carichi di lavoro, ad esempio la registrazione e i dati IoT.

Gestione dei ritenti

Le applicazioni che comunicano con i servizi in esecuzione nel cloud devono essere sensibili a eventi e errori non pianificati che possono verificarsi. Questi errori possono essere temporanei o persistenti, che vanno da una momentanea perdita di connettività a un'interruzione significativa a causa di una calamità naturale. È importante progettare applicazioni cloud con la gestione appropriata dei tentativi per ottimizzare la disponibilità e migliorare la stabilità complessiva delle applicazioni.

Richieste di lettura

Se l'area primaria non è più disponibile, le richieste di lettura possono essere reindirizzate all'archiviazione secondaria. Come indicato in precedenza, l'applicazione deve essere accettabile per leggere i dati non aggiornati. La libreria client di Archiviazione di Azure offre opzioni per gestire i tentativi e reindirizzare le richieste di lettura a un'area secondaria.

In questo esempio la gestione dei tentativi per l'archiviazione BLOB è configurata nella BlobClientOptions classe e verrà applicata all'oggetto BlobServiceClient creato usando queste opzioni di configurazione. Questa configurazione è un approccio primario e secondario , in cui i tentativi di richiesta di lettura dall'area primaria vengono reindirizzati all'area secondaria. Questo approccio è ottimale quando si prevede che gli errori nell'area primaria siano temporanei.

string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");

// Provide the client configuration options for connecting to Azure Blob storage
BlobClientOptions blobClientOptions = new BlobClientOptions()
{
    Retry = {
        // The delay between retry attempts for a fixed approach or the delay
        // on which to base calculations for a backoff-based approach
        Delay = TimeSpan.FromSeconds(2),

        // The maximum number of retry attempts before giving up
        MaxRetries = 5,

        // The approach to use for calculating retry delays
        Mode = RetryMode.Exponential,

        // The maximum permissible delay between retry attempts
        MaxDelay = TimeSpan.FromSeconds(10)
    },

    // If the GeoRedundantSecondaryUri property is set, the secondary Uri will be used for 
    // GET or HEAD requests during retries.
    // If the status of the response from the secondary Uri is a 404, then subsequent retries
    // for the request will not use the secondary Uri again, as this indicates that the resource 
    // may not have propagated there yet.
    // Otherwise, subsequent retries will alternate back and forth between primary and secondary Uri.
    GeoRedundantSecondaryUri = secondaryAccountUri
};

// Create a BlobServiceClient object using the configuration options above
BlobServiceClient blobServiceClient = new BlobServiceClient(primaryAccountUri, new DefaultAzureCredential(), blobClientOptions);

Se si determina che è probabile che l'area primaria non sia disponibile per un lungo periodo di tempo, è possibile configurare tutte le richieste di lettura in modo che puntino all'area secondaria. Questa configurazione è un approccio secondario. Come illustrato in precedenza, è necessaria una strategia per gestire le richieste di aggiornamento durante questo periodo e un modo per informare gli utenti che vengono elaborate solo le richieste di lettura. In questo esempio viene creata una nuova istanza di che usa l'endpoint dell'area BlobServiceClient secondaria.

string accountName = "<YOURSTORAGEACCOUNTNAME>";
Uri primaryAccountUri = new Uri($"https://{accountName}.blob.core.windows.net/");
Uri secondaryAccountUri = new Uri($"https://{accountName}-secondary.blob.core.windows.net/");

// Create a BlobServiceClient object pointed at the secondary Uri
// Use blobServiceClientSecondary only when issuing read requests, as secondary storage is read-only
BlobServiceClient blobServiceClientSecondary = new BlobServiceClient(secondaryAccountUri, new DefaultAzureCredential(), blobClientOptions);

Sapere quando passare alla modalità di sola lettura e alle richieste secondarie fa parte di un modello di progettazione architetturale denominato modello interruttore, che verrà illustrato in una sezione successiva.

Aggiornare le richieste

Le richieste di aggiornamento non possono essere reindirizzate all'archiviazione secondaria, che è di sola lettura. Come descritto in precedenza, l'applicazione deve essere in grado di gestire le richieste di aggiornamento quando l'area primaria non è disponibile.

Il pattern Circuit Breaker può essere applicato anche alle richieste di aggiornamento. Per gestire gli errori delle richieste di aggiornamento, è possibile impostare una soglia nel codice, ad esempio 10 errori consecutivi, e tenere traccia del numero di errori per le richieste all'area primaria. Una volta raggiunta la soglia, è possibile passare all'applicazione in modalità di sola lettura in modo che le richieste di aggiornamento all'area primaria non vengano più rilasciate.

Come implementare il modello interruttore

La gestione degli errori che possono richiedere una quantità variabile di tempo per il ripristino da è parte di un modello di progettazione architetturale denominato modello interruttore. L'implementazione corretta di questo modello può impedire a un'applicazione di tentare ripetutamente di eseguire un'operazione che potrebbe non riuscire, migliorando così la stabilità e la resilienza delle applicazioni.

Un aspetto del modello interruttore consiste nell'identificare quando si verifica un problema continuo con un endpoint primario. Per determinare questa determinazione, è possibile monitorare la frequenza con cui il client rileva errori riprovabili. Poiché ogni scenario è diverso, è necessario determinare una soglia appropriata da usare per la decisione di passare all'endpoint secondario ed eseguire l'applicazione in modalità di sola lettura.

Ad esempio, è possibile decidere di eseguire l'opzione se sono presenti 10 errori consecutivi nell'area primaria. È possibile tenere traccia di questo problema mantenendo un conteggio degli errori nel codice. Se si verifica un esito positivo prima di raggiungere la soglia, impostare il conteggio su zero. Se il conteggio raggiunge la soglia, passare all'applicazione per usare l'area secondaria per le richieste di lettura.

Come approccio alternativo, è possibile decidere di implementare un componente di monitoraggio personalizzato nell'applicazione. Questo componente potrebbe eseguire continuamente il ping dell'endpoint di archiviazione primario con richieste di lettura semplici (ad esempio la lettura di un BLOB di piccole dimensioni) per determinarne l'integrità. Questo approccio richiederebbe alcune risorse, ma non una quantità significativa. Quando viene rilevato un problema che raggiunge la soglia, passare alle richieste di sola lettura secondarie e alla modalità di sola lettura. Per questo scenario, quando si esegue il ping dell'endpoint di archiviazione primario, è possibile tornare all'area primaria e continuare a consentire gli aggiornamenti.

La soglia di errore usata per determinare quando eseguire il passaggio può variare da servizio a servizio all'interno dell'applicazione, pertanto è consigliabile valutarne la configurazione.

Un'altra considerazione è come gestire più istanze di un'applicazione e cosa fare quando si rilevano errori ritentabili in ogni istanza. Ad esempio, potrebbero essere presenti 20 macchine virtuali in esecuzione con la stessa applicazione caricata. Ogni istanza viene gestita separatamente? Se un'istanza inizia a riscontrare problemi, limitare la risposta a una sola istanza? Oppure si vuole che tutte le istanze rispondano nello stesso modo in cui un'istanza presenta un problema? La gestione delle istanze separatamente è molto più semplice rispetto al tentativo di coordinare la risposta tra di esse, ma l'approccio dipende dall'architettura dell'applicazione.

Gestione dei dati coerenti alla fine

L'archiviazione con ridondanza geografica funziona replicando le transazioni dal database primario all'area secondaria. Il processo di replica garantisce che i dati nell'area secondaria siano alla fine coerenti. Ciò significa che tutte le transazioni nell'area primaria verranno visualizzate nell'area secondaria, ma che potrebbero verificarsi un ritardo prima che vengano visualizzate. Non esiste inoltre alcuna garanzia che le transazioni arrivino nell'area secondaria nello stesso ordine in cui sono state originariamente applicate nell'area primaria. Se le transazioni arrivano nell'area secondaria non in ordine, è possibile considerare i dati nell'area secondaria in uno stato incoerente fino a quando il servizio non viene aggiornato.

L'esempio seguente per l'archiviazione tabelle di Azure mostra cosa può accadere quando si aggiornano i dettagli di un dipendente per renderli membri del ruolo di amministratore. Ai fini di questo esempio, è necessario aggiornare l'entità dipendente e aggiornare l'entità ruolo di amministratore con un conteggio del numero totale di amministratori. Si noti che gli aggiornamenti vengono applicati in ordine non ordinato nell'area secondaria.

Tempo transazioni Replicazione Ultimo orario di sincronizzazione Risultato
T0 Transazione A:
Inserisci dipendente
entità nel database primario
Transazione A inserita nel database primario,
non ancora replicato.
T1 Transazione A
replicato in
secondario
T1 Transazione A replicata nel database secondario.
Ora dell'ultima sincronizzazione aggiornata.
T2 Transazione B:
Aggiornamento
entità dipendente
nel primario
T1 Transazione B scritta nel database primario,
non ancora replicato.
T3 Transazione C:
Aggiornare
amministratore
entità ruolo in
primario
T1 Transazione C scritta nel database primario,
non ancora replicato.
T4 Transazione C
replicato in
secondario
T1 Transazione C replicata nel database secondario.
LastSyncTime non aggiornato perché
la transazione B non è stata ancora replicata.
T5 Leggere le entità
dal secondario
T1 Si ottiene il valore obsoleto per il dipendente
entità perché la transazione B non è stata completata
ancora non replicato. Si ottiene il nuovo valore per
funzione di amministratore perché C ha
replicato. L'ora dell'ultima sincronizzazione non è ancora avvenuta.
è stato aggiornato a causa della transazione B
non è stato replicato. Si può dire che
L'entità del ruolo di amministratore non è coerente
perché la data/ora dell'entità è posteriore
L'ultima ora di sincronizzazione.
T6 Transazione B
replicato in
secondario
T6 T6: tutte le transazioni tramite C hanno
è stato replicato, Ultima ora di sincronizzazione
è aggiornato.

In questo esempio, si supponga che il client, a T5, passi alla lettura dall'area secondaria. Può leggere correttamente l'entità ruolo amministratore in questo momento, ma l'entità contiene un valore per il conteggio degli amministratori che non è coerente con il numero di entità dipendente contrassegnate come amministratori nell'area secondaria in questo momento. Il client potrebbe visualizzare questo valore, con il rischio che le informazioni siano incoerenti. In alternativa, il client potrebbe tentare di determinare che il ruolo di amministratore è in uno stato potenzialmente incoerente perché gli aggiornamenti sono stati eseguiti fuori ordine e quindi informare l'utente di questo fatto.

Per determinare se un account di archiviazione ha dati potenzialmente incoerenti, il client può controllare il valore della proprietà Ora ultima sincronizzazione . Ora ultima sincronizzazione indica l'ora dell'ultima coerenza dei dati nell'area secondaria e dell'applicazione di tutte le transazioni precedenti a quel momento. Nell'esempio illustrato in precedenza, dopo che il servizio inserisce l'entità dipendente nell'area secondaria, l'ora dell'ultima sincronizzazione viene impostata su T1. Rimane in T1 fino a quando il servizio aggiorna l'entità dipendente nell'area secondaria quando è impostata su T6. Se il client recupera l'ora dell'ultima sincronizzazione in cui legge l'entità in T5, può confrontarla con il timestamp sull'entità. Se il timestamp dell'entità è successivo all'ora dell'ultima sincronizzazione, l'entità si trova in uno stato potenzialmente incoerente ed è possibile eseguire l'azione appropriata. Per usare questo campo è necessario sapere quando è stato completato l'ultimo aggiornamento al database primario.

Per informazioni su come controllare l'ora dell'ultima sincronizzazione, vedere Controllare la proprietà Ora ultima sincronizzazione per un account di archiviazione.

Prova

È importante verificare che l'applicazione si comporti come previsto quando rileva errori riprovabili. Ad esempio, è necessario verificare che l'applicazione passi all'area secondaria quando rileva un problema e quindi torna indietro quando l'area primaria diventa nuovamente disponibile. Per testare correttamente questo comportamento, è necessario un modo per simulare errori riprovabili e controllare la frequenza con cui si verificano.

Un'opzione consiste nell'usare Fiddler per intercettare e modificare le risposte HTTP in uno script. Questo script può identificare le risposte provenienti dall'endpoint primario e modificare il codice di stato HTTP in uno che la libreria client di archiviazione riconosce come errore riprovabile. Questo frammento di codice mostra un semplice esempio di script Fiddler che intercetta le risposte alle richieste di lettura nella tabella employeedata per restituire uno stato 502:

static function OnBeforeResponse(oSession: Session) {
    ...
    if ((oSession.hostname == "\[YOURSTORAGEACCOUNTNAME\].table.core.windows.net")
      && (oSession.PathAndQuery.StartsWith("/employeedata?$filter"))) {
        oSession.responseCode = 502;
    }
}

È possibile estendere questo esempio per intercettare una gamma più ampia di richieste e modificare solo il responseCode su alcuni di essi per simulare meglio uno scenario reale. Per ulteriori informazioni sulla personalizzazione degli script Fiddler, consultare Modifica di una richiesta o risposta nella documentazione di Fiddler.

Se sono state configurate soglie configurabili per il passaggio dell'applicazione in sola lettura, sarà più semplice testare il comportamento con volumi di transazioni non di produzione.


Passaggi successivi

Per un esempio completo che mostra come passare avanti e indietro tra gli endpoint primari e secondari, vedere Esempi di Azure - Uso dello schema del Circuit Breaker con RA-GRS archiviazione.