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ą dokładnie 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 awarii regionalnej. Konta magazynowe zaprojektowane do replikacji geograficznej nadmiarowej są synchronicznie replikowane w regionie podstawowym i asynchronicznie replikowane do regionu pomocniczego, oddalonego o setki kilometrów.
Usługa Azure Storage oferuje dwie opcje replikacji geograficznie nadmiarowej: geograficznie nadmiarowe przechowywanie (GRS) i geograficzne strefowo nadmiarowe przechowywanie (GZRS). Aby korzystać z opcji georedundancji w usłudze Azure Storage, upewnij się, że konto magazynu jest skonfigurowane na potrzeby georedundantnego magazynu z dostępem do odczytu (RA-GRS) lub geostrefowego magazynu z dostępem do odczytu (RA-GZRS). Jeśli to nie jest prawda, możesz dowiedzieć się więcej o tym, jak zmienić typ replikacji konta przechowywania.
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łączać 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 odczytywanie 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 RA-GRS lub RA-GZRS:
Kopia danych przechowywanych w regionie podstawowym tylko do odczytu jest asynchronicznie replikowana w regionie pomocniczym. Ta asynchroniczna replikacja oznacza, że kopia tylko do odczytu w regionie pomocniczym jest ostatecznie zgodna 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ę tak, aby wysyłała żądania 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 zostanie przywrócony dla kont GRS i RA-GRS. Aby uzyskać więcej informacji, zobacz Odzyskiwanie po awarii i 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że się zdarzyć, że region podstawowy stanie się niedostępny przed ukończeniem replikacji aktualizacji do regionu pomocniczego.
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 sposobie sprawdzania właściwości Czas ostatniej synchronizacji w celu oceny wszelkich rozbieżności między danymi w regionach podstawowych i pomocniczych.
Zarządzanie usługami oddzielnie lub razem
Chociaż jest mało prawdopodobne, istnieje możliwość, że jedna usługa (obiekty blob, kolejki, tabele lub pliki) stanie 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ć ponowne próby ogólne 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. W ten sposób 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 ponowienia prób dla usług magazynu jednocześnie, żądania zarówno do usług obiektów blob, jak i kolejek zostaną dotknięte, jeśli któraś z usług zwróci błąd, który można ponowić.
Ostatecznie ta decyzja zależy od złożoności aplikacji. Możesz preferować obsługę błędów 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 nieudane żądania aktualizacji. Jeśli region podstawowy zakończy się niepowodzeniem, żądania odczytu można przekierować 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 mogła 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. Kiedy nadejdzie żądanie aktualizacji, możesz je pominąć i zwrócić użytkownikowi odpowiednią odpowiedź. 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 dla każdej usługi z osobna. Można na przykład 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 uruchomić aplikację w trybie tylko do odczytu i przełączyć na pomocnicze centrum danych, aby upewnić się, że nikt nie uzyskuje dostępu do danych w regionie podstawowym podczas przeprowadzania uaktualnień.
Obsługa aktualizacji podczas pracy w trybie tylko do odczytu
Istnieje wiele sposobów obsługi żądań aktualizacji podczas uruchamiania 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ć ich, ż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.
Możesz umieścić aktualizacje w kolejce w innym regionie. W takim przypadku należy umieścić oczekujące żądania aktualizacji w kolejce w innym regionie, a następnie przetworzyć te żądania po ponownym uruchomieniu podstawowego centrum danych. W tym scenariuszu należy poinformować użytkownika, że żądanie aktualizacji jest kolejkowane 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, począwszy od chwilowej utraty łączności do znacznej awarii z powodu klęski żywiołowej. Ważne jest, aby projektować aplikacje w chmurze z odpowiednią obsługą ponawiania prób, aby zmaksymalizować dostępność i poprawić ogólną stabilność aplikacji.
Przeczytaj żądania
Jeśli region podstawowy stanie się niedostępny, żądania odczytu mogą być przekierowywane do magazynu pomocniczego. Jak wspomniano wcześniej, aplikacja musi akceptować możliwość odczytywania nieaktualnych danych. 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 BlobServiceClient
tworzonego przy użyciu tych opcji konfiguracji. Ta konfiguracja jest podejściem najpierw podstawowym, potem pomocniczym, gdzie powtarzane żądania odczytu z regionu podstawowego są przekierowywane do regionu pomocniczego. Takie podejście jest najlepsze, gdy błędy 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 tak, aby wskazywały na region pomocniczy. Ta konfiguracja jest podejściem wyłącznie drugorzędnym. 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 tworzymy nowe wystąpienie BlobServiceClient
, które korzysta z punktu końcowego 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 tylko drugorzędnych żądań jest częścią wzorca projektowania architektury nazywanego wzorcem obwodu przerywanego, który zostanie omówiony w dalszej części.
Żądania aktualizacji
Nie można przekierowywać żądań aktualizacji do pomocniczego magazynu, 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 obwodu 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 niepowodzeń, i śledzić liczbę błędów dla żą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ż wystawione.
Jak zaimplementować wzorzec "Circuit Breaker" w programowaniu
Obsługa awarii, które mogą wymagać różnej ilości czasu na naprawę, jest częścią wzorca projektowego architektury nazywanego wzorcem Circuit Breaker. 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, gdy 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 z możliwością ponawiania prób. Ponieważ każdy scenariusz jest inny, należy określić odpowiedni próg, który ma zostać użyty do podjęcia 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łączenia, jeśli w regionie podstawowym nastąpi 10 kolejnych awarii. Możesz to śledzić, zachowując liczbę błędów w kodzie. Jeśli przed osiągnięciem progu wystąpi sukces, ustaw liczbę z powrotem na zero. Jeśli liczba osiągnie próg, przełącz aplikację, aby używała regionu pomocniczego dla żądań odczytu.
Alternatywnie możesz zdecydować się na zaimplementowanie niestandardowego składnika monitorowania w aplikacji. Ten składnik może stale wysyłać polecenia ping do podstawowego punktu końcowego magazynu z prostymi żądaniami odczytu (takimi jak odczytywanie małego obiektu blob) w celu określenia jego kondycji. Takie podejście wymagałoby pewnych zasobów, ale nie znacznej kwoty. Po wykryciu problemu, który osiągnie Twój próg, przełączysz się na tylko pomocnicze żądania i tryb tylko do odczytu. W tym scenariuszu, gdy polecenie ping do podstawowego punktu końcowego magazynu zakończy się pomyślnie, możesz ponownie przełączyć się z powrotem do regionu podstawowego i kontynuować zezwalanie na aktualizacje.
Próg błędu używany do określenia, kiedy dokonać przełączenia, może się różnić w zależności od usługi w aplikacji, dlatego warto rozważyć możliwość skonfigurowania tych parametrów.
Innym zagadnieniem jest obsługa wielu wystąpień aplikacji oraz czynności, które należy wykonać po wykryciu błędów możliwych do ponowienia próby w każdym wystąpieniu. Na przykład może istnieć 20 maszyn wirtualnych z uruchomioną tą samą aplikacją. 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
Geograficznie redundantne przechowywanie działa przez replikację transakcji z regionu podstawowego do regionu wtórnego. 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 może wystąpić opóźnienie przed ich pojawieniem się. Nie ma również gwarancji, że transakcje pojawią się w regionie pomocniczym w tej samej kolejności, w której 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 nie nadrobi zaległości.
W poniższym przykładzie usługi Azure Table Storage pokazano, co może się zdarzyć, gdy zaktualizujesz szczegóły pracownika, aby zostać członkiem roli administratora. W tym przykładzie wymaga to zaktualizowania encji pracownika i zaktualizowania encji roli administratora poprzez podanie łącznej liczby administratorów. Zwróć uwagę, że aktualizacje są stosowane w innej kolejności w regionie dodatkowym.
Czas | transakcja | Replicacja | czas ostatniej synchronizacji | Wynik |
---|---|---|---|---|
T0 | Transakcja A: Wstaw pracownika podmiot podstawowy |
Transakcja A została wstawiona do głównej. jeszcze nie zreplikowany. |
||
T1 | Transakcja A zreplikowane na wtórny |
T1 | Transakcja A została zreplikowana do systemu pomocniczego. Czas ostatniej synchronizacji został zaktualizowany. |
|
T2 | Transakcja B: Aktualizacja jednostka pracownika w szkole podstawowej |
T1 | Transakcja B zapisana do bazy danych podstawowej jeszcze nie zreplikowane. |
|
T3 | Transakcja C: Aktualizacja zarządca jednostka roli w podstawowy |
T1 | Transakcja C zapisana na podstawowym serwerze. jeszcze nie są replikowane. |
|
T4 | Transakcja C zreplikowane do wtórny |
T1 | Transakcja C replikowana na wtórną. Czas ostatniej synchronizacji nie został zaktualizowany, ponieważ transakcja B nie została jeszcze zreplikowana. |
|
T5 | Odczytywanie jednostek z drugorzędnego |
T1 | Otrzymujesz nieaktualną wartość dla pracownika jednostka, ponieważ transakcja B jeszcze się nie zakończyła jeszcze nie zreplikowane Otrzymasz nową wartość dla jednostka roli administratora, ponieważ język C ma Replikowane. Czas ostatniej synchronizacji nadal się nie zaktualizował została zaktualizowana, ponieważ transakcja B nie został zreplikowany. Możesz powiedzieć Encja roli administratora jest niespójna ponieważ data/godzina jednostki przypada po czas ostatniej synchronizacji. |
|
T6 | Transakcja B skopiowane do wtórny |
T6 |
T6 — wszystkie transakcje za pośrednictwem języka C mają został zreplikowany, Czas ostatniej synchronizacji jest aktualizowany. |
W tym przykładzie przyjęto założenie, że klient przełącza się do odczytu z regionu pomocniczego w momencie T5. Obecnie można pomyślnie odczytać element roli administratora, ale element zawiera wartość dla liczby administratorów, których liczba nie jest zgodna 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 magazynowania ma potencjalnie niespójne dane, klient może zweryfikować parametr 'Czas ostatniej synchronizacji'. Czas ostatniej synchronizacji informuje, kiedy dane w regionie pomocniczym były ostatnio spójne i 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 na T1 do momentu, gdy usługa zaktualizuje jednostkę pracownika w regionie pomocniczym do momentu ustawienia jej na T6. Jeśli klient pobiera czas ostatniej synchronizacji podczas odczytywania jednostki w 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 podjąć odpowiednią akcję. Użycie tego pola wymaga wiedzy, kiedy ostatnia aktualizacja źródła głównego została zakończona.
Aby dowiedzieć się, jak sprawdzić czas ostatniej synchronizacji, zobacz Sprawdzanie właściwości Czas ostatniej synchronizacji dla konta magazynowego.
Testowanie
Ważne jest, aby sprawdzić, czy aplikacja zachowuje się zgodnie z oczekiwaniami, gdy napotka błędy z możliwością ponawiania próby. 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, należy zasymulować błędy z możliwością ponawiania próby i kontrolować, jak często występują.
Jedną z opcji jest użycie programu Fiddler do przechwytywania i modyfikowania odpowiedzi HTTP w skrypcie. 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 ponowienia próby. Ten fragment kodu przedstawia prosty przykład skryptu programu Fiddler, który przechwytuje odpowiedzi na żądania odczytu dla 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;
}
}
Ten przykład można rozszerzyć, aby przechwycić szerszy zakres żądań i zmienić tylko kod odpowiedzi 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 uzyskać pełny przykład pokazujący, jak przełączać się między głównymi a pomocniczymi punktami końcowymi, zobacz Azure Samples — Using the Circuit Breaker Pattern with RA-GRS storage (Przykłady platformy Azure — używanie wzorca wyłącznika z magazynem RA-GRS).