Używanie nadmiarowości geograficznej do projektowania aplikacji o wysokiej dostępności

Infrastruktury oparte na chmurze, takie jak Azure Storage, zapewniają wysoce dostępną i trwałą platformę do hostowania danych i aplikacji. Deweloperzy aplikacji opartych na chmurze muszą starannie rozważyć sposób wykorzystania tej platformy w celu zmaksymalizowania tych korzyści dla użytkowników. Usługa Azure Storage oferuje opcje nadmiarowości geograficznej, aby zapewnić wysoką dostępność nawet podczas regionalnej awarii. Konta magazynu skonfigurowane do replikacji geograficznie nadmiarowej są synchronicznie replikowane w regionie podstawowym i asynchronicznie replikowane do regionu pomocniczego, który jest oddalony o setki kilometrów.

Usługa Azure Storage oferuje dwie opcje replikacji geograficznie nadmiarowej: magazyn geograficznie nadmiarowy (GRS) i magazyn geograficznie nadmiarowy (GZRS). Aby korzystać z opcji nadmiarowości geograficznej usługi Azure Storage, upewnij się, że konto magazynu jest skonfigurowane dla magazynu geograficznie nadmiarowego dostępnego do odczytu (RA-GRS) lub magazynu geograficznie nadmiarowego dostępnego do odczytu (RA-GZRS). Jeśli tak nie jest, możesz dowiedzieć się więcej o tym, jak zmienić typ replikacji konta magazynu.

W tym artykule pokazano, jak zaprojektować aplikację, która będzie nadal działać, choć w ograniczonej pojemności, nawet w przypadku znacznej awarii w regionie podstawowym. Jeśli region podstawowy stanie się niedostępny, aplikacja może bezproblemowo przełączyć się w celu wykonywania operacji odczytu w regionie pomocniczym do momentu ponownego reagowania regionu podstawowego.

Zagadnienia dotyczące projektowania aplikacji

Aplikację można zaprojektować tak, aby obsługiwała błędy przejściowe lub znaczące awarie, odczytując z regionu pomocniczego, gdy występuje problem, który zakłóca odczyt z regionu podstawowego. Gdy region podstawowy jest ponownie dostępny, aplikacja może wrócić do odczytu z regionu podstawowego.

Należy pamiętać o tych kluczowych kwestiach podczas projektowania aplikacji pod kątem dostępności i odporności przy użyciu magazynu RA-GRS lub RA-GZRS:

  • Kopia danych przechowywanych w regionie podstawowym tylko do odczytu jest replikowana asynchronicznie w regionie pomocniczym. Ta replikacja asynchroniczna oznacza, że kopia tylko do odczytu w regionie pomocniczym jest ostatecznie spójna z danymi w regionie podstawowym. Usługa magazynu określa lokalizację regionu pomocniczego.

  • Biblioteki klienta usługi Azure Storage umożliwiają wykonywanie żądań odczytu i aktualizacji względem punktu końcowego regionu podstawowego. Jeśli region podstawowy jest niedostępny, możesz automatycznie przekierowywać żądania odczytu do regionu pomocniczego. Możesz również skonfigurować aplikację do wysyłania żądań odczytu bezpośrednio do regionu pomocniczego, nawet jeśli jest dostępny region podstawowy.

  • Jeśli region podstawowy stanie się niedostępny, możesz zainicjować tryb failover konta. Po przejściu w tryb failover do regionu pomocniczego wpisy DNS wskazujące region podstawowy są zmieniane tak, aby wskazywały region pomocniczy. Po zakończeniu pracy w trybie failover dostęp do zapisu jest przywracany dla kont GRS i RA-GRS. Aby uzyskać więcej informacji, zobacz Odzyskiwanie po awarii i tryb failover konta magazynu.

Praca z ostatecznie spójnymi danymi

Proponowane rozwiązanie zakłada, że dopuszczalne jest zwracanie potencjalnie nieaktualnych danych do aplikacji wywołującej. Ponieważ dane w regionie pomocniczym są ostatecznie spójne, możliwe, że region podstawowy może stać się niedostępny, zanim aktualizacja regionu pomocniczego zakończy replikację.

Załóżmy na przykład, że klient pomyślnie przesyła aktualizację, ale region podstawowy kończy się niepowodzeniem, zanim aktualizacja zostanie rozpropagowana do regionu pomocniczego. Gdy klient poprosi o odczytanie danych z powrotem, otrzyma nieaktualne dane z regionu pomocniczego zamiast zaktualizowanych danych. Podczas projektowania aplikacji musisz zdecydować, czy to zachowanie jest akceptowalne. Jeśli tak jest, należy również rozważyć sposób powiadamiania użytkownika.

W dalszej części tego artykułu dowiesz się więcej o obsłudze ostatecznie spójnych danych i sposobach sprawdzania właściwości Czas ostatniej synchronizacji , aby ocenić wszelkie rozbieżności między danymi w regionach podstawowych i pomocniczych.

Obsługa usług oddzielnie lub razem

Chociaż jest mało prawdopodobne, istnieje możliwość, aby jedna usługa (obiekty blob, kolejki, tabele lub pliki) stała się niedostępna, podczas gdy inne usługi są nadal w pełni funkcjonalne. Można obsługiwać ponawianie prób dla każdej usługi oddzielnie lub obsługiwać ponawianie prób dla wszystkich usług magazynu razem.

Jeśli na przykład używasz kolejek i obiektów blob w aplikacji, możesz zdecydować się na umieszczenie oddzielnego kodu w celu obsługi błędów możliwych do ponawiania dla każdej usługi. Dzięki temu błąd usługi blob będzie mieć wpływ tylko na część aplikacji, która zajmuje się obiektami blob, pozostawiając kolejki, aby nadal działały normalnie. Jeśli jednak zdecydujesz się obsłużyć wszystkie ponowne próby usługi magazynu razem, żądania do usług obiektów blob i kolejek będą miały wpływ, jeśli którakolwiek z usług zwróci błąd możliwy do ponowienia próby.

Ostatecznie ta decyzja zależy od złożoności aplikacji. Wolisz obsługiwać błędy według usługi, aby ograniczyć wpływ ponownych prób. Możesz też zdecydować się na przekierowanie żądań odczytu dla wszystkich usług magazynu do regionu pomocniczego w przypadku wykrycia problemu z dowolną usługą magazynu w regionie podstawowym.

Uruchamianie aplikacji w trybie tylko do odczytu

Aby skutecznie przygotować się do awarii w regionie podstawowym, aplikacja musi być w stanie obsłużyć zarówno nieudane żądania odczytu, jak i żądania aktualizacji, które zakończyły się niepowodzeniem. Jeśli region podstawowy ulegnie awarii, żądania odczytu mogą być przekierowywane do regionu pomocniczego. Nie można jednak przekierowywać żądań aktualizacji, ponieważ replikowane dane w regionie pomocniczym są tylko do odczytu. Z tego powodu należy zaprojektować aplikację, aby móc działać w trybie tylko do odczytu.

Można na przykład ustawić flagę sprawdzaną przed przesłaniem żądań aktualizacji do usługi Azure Storage. Po wysłaniu żądania aktualizacji możesz pominąć żądanie i zwrócić odpowiednią odpowiedź do użytkownika. Możesz nawet całkowicie wyłączyć niektóre funkcje do momentu rozwiązania problemu i powiadomić użytkowników, że funkcje są tymczasowo niedostępne.

Jeśli zdecydujesz się obsługiwać błędy dla każdej usługi oddzielnie, musisz również obsługiwać możliwość uruchamiania aplikacji w trybie tylko do odczytu według usługi. Na przykład można skonfigurować flagi tylko do odczytu dla każdej usługi. Następnie możesz włączyć lub wyłączyć flagi w kodzie zgodnie z potrzebami.

Możliwość uruchamiania aplikacji w trybie tylko do odczytu umożliwia również zapewnienie ograniczonej funkcjonalności podczas uaktualniania głównej aplikacji. Możesz wyzwolić uruchamianie aplikacji w trybie tylko do odczytu i wskazywać pomocnicze centrum danych, zapewniając, że nikt nie uzyskuje dostępu do danych w regionie podstawowym podczas wprowadzania uaktualnień.

Obsługa aktualizacji w trybie tylko do odczytu

Istnieje wiele sposobów obsługi żądań aktualizacji w trybie tylko do odczytu. Ta sekcja koncentruje się na kilku ogólnych wzorcach, które należy wziąć pod uwagę.

  • Możesz odpowiedzieć użytkownikowi i powiadomić go, że żądania aktualizacji nie są obecnie przetwarzane. Na przykład system zarządzania kontaktami może umożliwić użytkownikom dostęp do informacji kontaktowych, ale nie wprowadzać aktualizacji.

  • Aktualizacje można w kolejce w innym regionie. W takim przypadku należy zapisać oczekujące żądania aktualizacji do kolejki w innym regionie, a następnie przetworzyć te żądania po ponownym przejściu do trybu online podstawowego centrum danych. W tym scenariuszu należy poinformować użytkownika, że żądanie aktualizacji jest w kolejce do późniejszego przetwarzania.

  • Aktualizacje można zapisywać na koncie magazynu w innym regionie. Gdy region podstawowy wróci do trybu online, możesz scalić te aktualizacje z danymi podstawowymi w zależności od struktury danych. Jeśli na przykład tworzysz oddzielne pliki z sygnaturą daty/godziny w nazwie, możesz skopiować te pliki z powrotem do regionu podstawowego. To rozwiązanie może dotyczyć obciążeń, takich jak rejestrowanie i dane IoT.

Obsługa ponownych prób

Aplikacje komunikujące się z usługami działającymi w chmurze muszą być wrażliwe na nieplanowane zdarzenia i błędy, które mogą wystąpić. Te błędy mogą być przejściowe lub trwałe, od chwilowej utraty łączności do znacznej awarii spowodowanej klęską żywiołową. Ważne jest, aby projektować aplikacje w chmurze z odpowiednią obsługą ponawiania prób, aby zmaksymalizować dostępność i zwiększyć ogólną stabilność aplikacji.

Odczytywanie żądań

Jeśli region podstawowy stanie się niedostępny, żądania odczytu mogą być przekierowywane do magazynu pomocniczego. Jak wspomniano wcześniej, aplikacja musi być akceptowalna, aby potencjalnie odczytywała nieaktualne dane. Biblioteka klienta usługi Azure Storage oferuje opcje obsługi ponownych prób i przekierowywania żądań odczytu do regionu pomocniczego.

W tym przykładzie obsługa ponawiania prób dla usługi Blob Storage jest skonfigurowana w BlobClientOptions klasie i będzie stosowana do obiektu, który BlobServiceClient utworzymy przy użyciu tych opcji konfiguracji. Ta konfiguracja jest podstawowym następnie podejściem pomocniczym , w którym ponawianie żądań odczytu z regionu podstawowego jest przekierowywane do regionu pomocniczego. Takie podejście jest najlepsze, gdy awarie w regionie podstawowym powinny być tymczasowe.

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

Jeśli ustalisz, że region podstawowy prawdopodobnie będzie niedostępny przez długi czas, możesz skonfigurować wszystkie żądania odczytu, aby wskazywały region pomocniczy. Ta konfiguracja jest podejściem tylko pomocniczym . Jak wspomniano wcześniej, potrzebna jest strategia obsługi żądań aktualizacji w tym czasie oraz sposób informowania użytkowników o przetwarzaniu tylko żądań odczytu. W tym przykładzie utworzymy nowe wystąpienie, którego BlobServiceClient używa punkt końcowy regionu pomocniczego.

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

Wiedza na temat przełączania się do trybu tylko do odczytu i pomocniczych żądań jest częścią wzorca projektowego architektury nazywanego wzorcem wyłącznika, który zostanie omówiony w późniejszej sekcji.

Żądania aktualizacji

Nie można przekierować żądań aktualizacji do magazynu pomocniczego, który jest tylko do odczytu. Jak opisano wcześniej, aplikacja musi mieć możliwość obsługi żądań aktualizacji , gdy region podstawowy jest niedostępny.

Wzorzec wyłącznika można również zastosować do żądań aktualizacji. Aby obsłużyć błędy żądania aktualizacji, można ustawić próg w kodzie, taki jak 10 kolejnych awarii, i śledzić liczbę błędów żądań do regionu podstawowego. Po osiągnięciu progu można przełączyć aplikację do trybu tylko do odczytu, aby żądania aktualizacji do regionu podstawowego nie zostały już wydane.

Jak zaimplementować wzorzec wyłącznika

Obsługa błędów, które mogą zająć zmienną ilość czasu do odzyskania, jest częścią wzorca projektowego architektury nazywanego wzorcem wyłącznika. Właściwa implementacja tego wzorca może uniemożliwić aplikacji wielokrotne wykonywanie operacji, która może zakończyć się niepowodzeniem, co zwiększa stabilność i odporność aplikacji.

Jednym z aspektów wzorca wyłącznika jest zidentyfikowanie, kiedy występuje ciągły problem z podstawowym punktem końcowym. Aby to określić, możesz monitorować, jak często klient napotyka błędy umożliwiające ponowienie próby. Ponieważ każdy scenariusz jest inny, należy określić odpowiedni próg, aby użyć decyzji o przełączeniu się do pomocniczego punktu końcowego i uruchomieniu aplikacji w trybie tylko do odczytu.

Na przykład możesz zdecydować się na wykonanie przełącznika, jeśli w regionie podstawowym występuje 10 kolejnych awarii. Możesz to śledzić, zachowując liczbę błędów w kodzie. Jeśli przed osiągnięciem progu wystąpi powodzenie, ustaw liczbę z powrotem na zero. Jeśli liczba osiągnie próg, przełącz aplikację tak, aby korzystała z regionu pomocniczego dla żądań odczytu.

Alternatywną metodą jest wdrożenie niestandardowego składnika monitorowania w aplikacji. Ten składnik może stale wysyłać polecenia ping do podstawowego punktu końcowego magazynu z trywialnymi żądaniami odczytu (takimi jak odczytywanie małego obiektu blob) w celu określenia jego kondycji. Takie podejście wymagałoby niektórych zasobów, ale nie znacznej kwoty. Po wykryciu problemu, który osiągnie próg, przełączysz się na pomocnicze tylko żądania odczytu i tryb tylko do odczytu. W tym scenariuszu podczas wysyłania polecenia ping do podstawowego punktu końcowego magazynu można ponownie przełączyć się z powrotem do regionu podstawowego i kontynuować zezwalanie na aktualizacje.

Próg błędu używany do określenia, kiedy przełączenie może się różnić od usługi do usługi w aplikacji, dlatego należy rozważyć ich skonfigurowanie parametrów.

Innym zagadnieniem jest sposób obsługi wielu wystąpień aplikacji oraz czynności, które należy wykonać po wykryciu błędów możliwych do ponawiania prób w każdym wystąpieniu. Na przykład może istnieć 20 maszyn wirtualnych z tą samą aplikacją załadowaną. Czy obsługujesz każde wystąpienie oddzielnie? Jeśli jedno wystąpienie zaczyna mieć problemy, czy chcesz ograniczyć odpowiedź tylko do tego jednego wystąpienia? Czy też chcesz, aby wszystkie wystąpienia odpowiadały w taki sam sposób, gdy jedno wystąpienie ma problem? Obsługa wystąpień oddzielnie jest znacznie prostsza niż próba koordynowania odpowiedzi między nimi, ale podejście będzie zależeć od architektury aplikacji.

Obsługa ostatecznie spójnych danych

Magazyn geograficznie nadmiarowy działa przez replikowanie transakcji z regionu podstawowego do regionu pomocniczego. Proces replikacji gwarantuje, że dane w regionie pomocniczym są ostatecznie spójne. Oznacza to, że wszystkie transakcje w regionie podstawowym zostaną ostatecznie wyświetlone w regionie pomocniczym, ale zanim pojawią się, może wystąpić opóźnienie. Nie ma również gwarancji, że transakcje dotrą do regionu pomocniczego w tej samej kolejności, co zostały pierwotnie zastosowane w regionie podstawowym. Jeśli transakcje docierają do regionu pomocniczego poza kolejnością, możesz rozważyć , że dane w regionie pomocniczym będą w stanie niespójnym, dopóki usługa się nie nadrobi.

W poniższym przykładzie usługi Azure Table Storage pokazano, co może się zdarzyć po zaktualizowaniu szczegółów pracownika, aby był członkiem roli administratora. W tym przykładzie jest to wymagane zaktualizowanie jednostki pracownika i zaktualizowanie jednostki roli administratora przy użyciu liczby administratorów całkowitej liczby administratorów. Zwróć uwagę, jak aktualizacje są stosowane poza kolejnością w regionie pomocniczym.

Godzina Transakcja Replikacja Czas ostatniej synchronizacji Wynik
T0 Transakcja A:
Wstaw pracownika
jednostka w jednostce podstawowej
Transakcja A wstawiona do elementu podstawowego,
nie został jeszcze zreplikowany.
T1 Transakcja A
zreplikowane do
Pomocniczy
T1 Transakcja A zreplikowana do pomocniczej.
Czas ostatniej synchronizacji został zaktualizowany.
T2 Transakcja B:
Aktualizacja
jednostka pracownika
w podstawowej
T1 Transakcja B zapisana w lokalizacji podstawowej,
nie został jeszcze zreplikowany.
T3 Transakcja C:
Aktualizacja
administrator
jednostka roli w
Podstawowy
T1 Transakcja C zapisana w lokalizacji podstawowej,
nie został jeszcze zreplikowany.
T4 Transakcja C
zreplikowane do
Pomocniczy
T1 Transakcja C replikowana do pomocniczej.
Funkcja LastSyncTime nie została zaktualizowana, ponieważ
transakcja B nie została jeszcze zreplikowana.
T5 Odczytywanie jednostek
z pomocniczego
T1 Otrzymujesz nieaktualną wartość dla pracownika
jednostka, ponieważ transakcja B nie ma
zreplikowane jeszcze. Otrzymasz nową wartość dla
jednostka roli administratora, ponieważ język C ma
Replikowane. Czas ostatniej synchronizacji nadal nie został przekroczony
została zaktualizowana, ponieważ transakcja B
nie został zreplikowany. Możesz powiedzieć
Jednostka roli administratora jest niespójna
ponieważ data/godzina jednostki jest po
czas ostatniej synchronizacji.
T6 Transakcja B
zreplikowane do
Pomocniczy
T6 T6 — wszystkie transakcje za pośrednictwem języka C mają
replikowane, czas ostatniej synchronizacji
jest aktualizowany.

W tym przykładzie przyjęto założenie, że klient przełącza się na odczyt z regionu pomocniczego w lokalizacji T5. W tej chwili można pomyślnie odczytać jednostkę roli administratora , ale jednostka zawiera wartość liczby administratorów, którzy nie są w tej chwili zgodni z liczbą jednostek pracowników oznaczonych jako administratorzy w regionie pomocniczym. Klient może wyświetlić tę wartość z ryzykiem, że informacje są niespójne. Alternatywnie klient może podjąć próbę ustalenia, że rola administratora jest w stanie potencjalnie niespójnym, ponieważ aktualizacje wystąpiły poza kolejnością, a następnie poinformować użytkownika o tym fakcie.

Aby określić, czy konto magazynu ma potencjalnie niespójne dane, klient może sprawdzić wartość właściwości Czas ostatniej synchronizacji . Czas ostatniej synchronizacji informuje o czasie, kiedy dane w regionie pomocniczym były ostatnio spójne, a kiedy usługa zastosowała wszystkie transakcje przed tym punktem w czasie. W powyższym przykładzie po wstawieniu jednostki pracownika w regionie pomocniczym czas ostatniej synchronizacji jest ustawiony na T1. Pozostaje w T1 , dopóki usługa nie zaktualizuje jednostki pracownika w regionie pomocniczym, gdy jest ustawiona na T6. Jeśli klient pobiera czas ostatniej synchronizacji podczas odczytywania jednostki w języku T5, może porównać go ze znacznikiem czasu w jednostce. Jeśli sygnatura czasowa jednostki jest późniejsza niż czas ostatniej synchronizacji, jednostka jest w stanie potencjalnie niespójnym i możesz wykonać odpowiednie działania. Użycie tego pola wymaga, aby wiedzieć, kiedy ostatnia aktualizacja podstawowego została ukończona.

Aby dowiedzieć się, jak sprawdzić czas ostatniej synchronizacji, zobacz Sprawdzanie właściwości Czas ostatniej synchronizacji dla konta magazynu.

Testowanie

Ważne jest, aby przetestować, czy aplikacja zachowuje się zgodnie z oczekiwaniami, gdy napotka błędy, które można ponowić. Na przykład należy przetestować, czy aplikacja przełącza się do regionu pomocniczego, gdy wykryje problem, a następnie przełącza się z powrotem, gdy region podstawowy stanie się ponownie dostępny. Aby prawidłowo przetestować to zachowanie, potrzebny jest sposób symulowania błędów możliwych do ponawiania i kontrolowania częstotliwości ich występowania.

Jedną z opcji jest użycie programu Fiddler do przechwytywania i modyfikowania odpowiedzi HTTP w skrycie. Ten skrypt może zidentyfikować odpowiedzi pochodzące z podstawowego punktu końcowego i zmienić kod stanu HTTP na taki, który biblioteka klienta usługi Storage rozpoznaje jako błąd możliwy do ponawiania. Ten fragment kodu przedstawia prosty przykład skryptu fiddler, który przechwytuje odpowiedzi na żądania odczytu względem tabeli employeedata w celu zwrócenia stanu 502:

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

Możesz rozszerzyć ten przykład, aby przechwycić szerszy zakres żądań i zmienić kod odpowiedzi tylko na niektóre z nich, aby lepiej symulować rzeczywisty scenariusz. Aby uzyskać więcej informacji na temat dostosowywania skryptów programu Fiddler, zobacz Modyfikowanie żądania lub odpowiedzi w dokumentacji programu Fiddler.

Jeśli skonfigurowaliśmy konfigurowalne progi przełączania aplikacji na tylko do odczytu, łatwiej będzie przetestować zachowanie przy użyciu woluminów transakcji nieprodukcyjnych.


Następne kroki

Aby zapoznać się z kompletnym przykładem pokazującym, jak zmienić przełącznik między podstawowymi i pomocniczymi punktami końcowymi, zobacz Przykłady platformy Azure — używanie wzorca wyłącznika z magazynem RA-GRS.