Ćwiczenie — implementowanie zasad ponawiania w aplikacji

Ukończone

Aplikacja do czatów zespołu została ulepszona tak, aby wykrywać błędy. W przypadku korzystania z bazy danych opartej na chmurze mogą wystąpić różne błędy przejściowe. W tym ćwiczeniu skoncentrujemy się na rozwiązywaniu problemów z połączeniem przez zaimplementowanie zasad ponawiania prób.

Jeśli błąd jest spowodowany problemami z połączeniem z bazą danych, przyjmujemy następującą strategię:

  • Jeśli błąd jest związany z siecią, szybko ponów próbę połączenia
  • Ponawiaj próbę ponownego połączenia co 60 sekund
  • Ponów próbę maksymalnie pięć razy
  • Po 5 próbach powiadom użytkownika końcowego o problemie bazy danych i zakończ pracę

W przypadku innych nieznanych błędów zamknij aplikację.

Używanie klasy do implementowania zasad ponawiania

  1. Uruchom następujące polecenie w usłudze Cloud Shell, aby przejść do folderu chatapp-retry języka C#.

    cd ~/mslearn-handle-transient-errors-in-your-app/csharp/chatapp-retry/
    
  2. Ta wersja zawiera pierwszą wersję roboczą kodowania klasy zasad ponawiania. Obejmuje ona plik konfiguracji appsettings.json, który umożliwia administratorom systemu ustawianie opóźnienia i liczby ponownych prób.

    {
        "number-of-retries": 5,
        "delay": 60000
    }
    
  3. Otwórz klasę RetryPolicy.

    code RetryPolicy.cs
    

    Poświęć chwilę na przeczytanie kodu w tej klasie.

    Klasa RetryPolicy śledzi liczbę ponownych prób i obsługuje dodawanie opóźnienia, zanim będzie można podjąć ponowną próbę pisania kodu. Większość logiki naszych zasad ponawiania znajduje się w metodzie CanRetry():

    using System;
    using Microsoft.Extensions.Configuration;
    using System.Threading;
    using System.Diagnostics;
    using System.IO;
    
    
    namespace csharp_chatapp_retry
    {
        public class RetryPolicy
        {
            private int currentTries = 0;
            static public IConfiguration configuration { get; set; }
    
            /// <summary>
            /// Constructor - reads config for number of retries and delay
            /// </summary>
            public RetryPolicy()
            {
                ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
                configurationBuilder.SetBasePath(Directory.GetCurrentDirectory());
                configurationBuilder.AddJsonFile("appsettings.json");
                configuration = configurationBuilder.Build();
            }
    
            /// <summary>
            /// Method to implement retry policy controlled by configuration
            /// </summary>
            /// <returns>Returns true if it's ok to retry</returns>
            public bool CanRetry()
            {
                // Keep track of current retries
                currentTries++;
                Console.WriteLine($"Retrying: {currentTries}");
    
                // Use a delay if this isn't the first try
                if (currentTries != 1)
                {
                    Thread.Sleep(int.Parse(configuration["delay"]));
                }
    
                if (currentTries < int.Parse(configuration["number-of-retries"])) {
                    return true;
                } else {
                    return false;
                }
            }
    
            public void ResetRetries()
            {
                currentTries = 0;
            }
        }
    }
    

    Metoda canRetry sprawdza liczbę ponownych prób, a następnie powraca true do kodu wywołującego, jeśli jest ok, aby ponowić próbę lub false czy aplikacja powinna teraz zostać zatrzymana.

    Teraz możemy użyć tej klasy w naszej aplikacji do zaimplementowania zasad ponawiania.

Dodawanie zasad ponawiania

  1. Wybierz trzy wielokropek (...) w prawym górnym rogu edytora, a następnie wybierz pozycję Otwórz plik .... Wybierz plik Program.cs w oknie dialogowym selektora plików i naciśnij klawisz Enter.

  2. Zaktualizuj zmienną connectionString w tej klasie na wartość znalezionych wcześniej parametrów połączenia bazy danych Azure Cosmos DB.

  3. Przewiń w dół do metody getAllChats().

    private static void getAllChats()
    {
        messages = database.GetCollection<ChatMessage>(collectionName);
        try
        {
            allMessages = messages.Find(new BsonDocument()).ToList();
            foreach (ChatMessage chat in allMessages)
            {
                Console.WriteLine($"{chat.Name}: {chat.Message}");
            }
            Console.WriteLine("\n");
        }
        catch (MongoDB.Driver.MongoConnectionException e)
        {
            diagnose(e);
        }
        catch (System.TimeoutException e)
        {
            diagnose(e);
        }
        catch (Exception e)
        {
            diagnose(e);
            throw e;
        }
    }
    
  4. Dodaj kod, aby ponowić próbę połączenia w dwóch blokach catch specyficznych dla bazy danych MongoDB.

        if (retries.CanRetry())
        {
            diagnose(e);
            getAllChats(); //retry
        } else {
            Console.WriteLine("Maximum retries - need to close.");
            throw e;
        }
    
  5. Metoda powinna być następującym kodem:

        private static void getAllChats()
        {
            messages = database.GetCollection<ChatMessage>(collectionName);
            try
            {
                allMessages = messages.Find(new BsonDocument()).ToList();
                foreach (ChatMessage chat in allMessages)
                {
                    Console.WriteLine(String.Format("{0}: {1}", chat.Name, chat.Message));
                }
                Console.WriteLine("\n");
            }
            catch (MongoDB.Driver.MongoConnectionException e)
            {
                if (retries.CanRetry())
                {
                    diagnose(e);
                    getAllChats(); //retry
                } else {
                    Console.WriteLine("Maximum retries - need to close.");
                    throw e;
                }
            }
            catch (System.TimeoutException e)
            {
                if (retries.CanRetry())
                {
                    diagnose(e);
                    getAllChats(); //retry
                } else {
                    Console.WriteLine("Maximum retries - need to close.");
                    throw e;
                }
            }
            catch (Exception e)
            {
                diagnose(e);
                throw e;
            }
        }
    
  6. Zdefiniuj obiekt ponownych prób i zresetuj go dla każdego wywołania do bazy danych. Dodaj deklarację dla obiektu ponownych prób przed metodą główną:

        private static RetryPolicy retries = new RetryPolicy();
    
  7. Zresetuj ponowne próby w pętli while metody Main() w pliku Program.cs, jak pokazano w poniższym fragmencie kodu.

    while(choice != 'Q' && choice != 'q')
    {
        retries.Reset();
        Console.WriteLine();
        ChatMessage newChat = new ChatMessage();
        switch (choice)
        {
            case 'N':
    
  8. Aby zapisać nasze zmiany, wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz kolejno pozycje Zamknij edytor i Zapisz.

  9. Skompiluj i uruchom aplikację.

    dotnet build
    dotnet run
    
  10. Aplikacja powinna zostać skompilowana i uruchomiona. Dodaj nowy komunikat, naciśnij N, a następnie wprowadź nazwę i komunikat.

  11. Wyświetl listę wszystkich komunikatów, naciskając klawisz R, a następnie klawisz Enter.

  12. Pozostaw uruchomioną aplikację.

Używanie klasy do implementowania zasad ponawiania

  1. W usłudze Cloud Shell przejdź do folderu chatapp-retry języka Java.

    cd ~/mslearn-handle-transient-errors-in-your-app/java/chatapp-retry/
    
  2. Ta wersja zawiera pierwszą wersję roboczą kodowania klasy zasad ponawiania. Obejmuje ona plik konfiguracji config.properties, który umożliwia administratorom systemu ustawianie opóźnienia i liczby ponownych prób.

    number_of_retries=5
    delay=60
    
  3. Otwórz klasę RetryPolicy.

    code RetryPolicy.java
    
  4. Klasa śledzi liczbę ponownych prób i obsługuje dodawanie opóźnienia, zanim będzie można podjąć ponowną próbę pisania kodu. Główna logika w metodzie canRetry:

    public boolean canRetry() throws InterruptedException {
        // Keep track of current retries
        currentTries++;
        System.out.printf("Retrying: %s\n", currentTries);
    
        // Use a delay if this isn't the first try
        if (currentTries != 1)
        {
            TimeUnit.SECONDS.sleep(Integer.parseInt(props.getProperty("delay")));
        }
    
        if (currentTries < Integer.parseInt(props.getProperty("number_of_retries"))) {
            return true;
        } else {
            return false;
        }
    }
    

    Metoda canRetry sprawdza liczbę ponownych prób, a następnie powraca true do kodu wywołującego, jeśli jest ok, aby ponowić próbę lub false czy aplikacja powinna teraz zostać zatrzymana.

  5. Teraz możemy użyć tej klasy, aby dodać zasady ponawiania prób do aplikacji do czatu.

Dodawanie zasad ponawiania

  1. Wybierz trzy wielokropki (...) w prawym górnym rogu edytora, a następnie wybierz pozycję Otwórz w oknie dialogowym wpisz javaChat.java, a następnie naciśnij klawisz Enter.

  2. Zlokalizuj metodę printAllMessages() w pliku javaChat.java.

  3. Zastąp implementację elementu printAllMessages następującym kodem. Ten kod dodaje logikę ponawiania prób do bloków catch bazy danych MongoDB.

        private static void printAllMessages (MongoCollection<Document> collection) throws InterruptedException {
            try {
                // Return all messages
                collection.find().forEach((Consumer<Document>) document -> {
                    System.out.printf("%s: %s\n", document.get("name"), document.get("message"));
                });
            }
            catch (com.mongodb.MongoCommandException e) {
                if (retries.canRetry())
                {
                    diagnose(e);
                    printAllMessages(collection); //retry
                } else {
                    System.out.println("Maximum retries - need to close.");
                    throw e;
                }
            }
            catch (com.mongodb.MongoSecurityException e) {
                if (retries.canRetry())
                {
                    diagnose(e);
                    printAllMessages(collection); //retry
                } else {
                    System.out.println("Maximum retries - need to close.");
                    throw e;
                }
            }
            catch (Exception e) {
                diagnose(e);
                throw e;
            }
        }
    
  4. Dodaj deklarację dla obiektu RetryPolicy przed metodą Main w pliku javaChat.java.

    private static RetryPolicy retries;
    
  5. Utwórz wystąpienie zmiennej retries wewnątrz metody Main:

        try{
            retries = new RetryPolicy();
        } catch(FileNotFoundException e) {
            e.printStackTrace();
        }
    
  6. Dodaj następujący wiersz kodu w górnej części pętli while, aby zresetować ponowne próby.

        retries.resetRetries();
    
  7. Zapisz plik i zamknij edytor. Użyj poleceń w menu ... w prawym górnym rogu edytora lub użyj klawiszy skrótu Ctrl+S, aby zapisać plik, i Ctrl+Q, aby zamknąć edytor.

  8. Skompiluj aplikację przy użyciu następującego polecenia:

    javac -cp .:lib/* -d . javaChat.java RetryPolicy.java
    
  9. Uruchom aplikację przy użyciu następującego polecenia w usłudze Cloud Shell.

    java -cp .:lib/* learn.javachatapp.javaChat
    

Używanie funkcji do implementowania zasad ponawiania

  1. W usłudze Cloud Shell przejdź do folderu chatapp-retry węzła.

    cd ~/mslearn-handle-transient-errors-in-your-app/node/chatapp-retry/
    
  2. Ta wersja zawiera pierwszą wersję roboczą kodowania klasy zasad ponawiania. Obejmuje ona plik konfiguracji appsettings.json, który umożliwia administratorom systemu ustawianie opóźnienia i liczby ponownych prób.

    {
        "number_of_retries": 5,
        "delay": 60000
    }
    
  3. Pobierz zależności.

    npm install
    
  4. Otwórz skrypt pliku retryPolicy.js.

    code retryPolicy.js
    
  5. Klasa śledzi liczbę ponownych prób i obsługuje dodawanie opóźnienia, zanim będzie można podjąć ponowną próbę pisania kodu. Główna logika w funkcji checkRetries:

    
    method.checkRetries = function() {
        this._currentTries = this._currentTries + 1;
        console.log('Retrying: ' + this._currentTries);
    
        // Use a delay if this isn't the first try
        if (this._currentTries != 1)
        {
            sleep(config.delay);
        }
    
        if (this._currentTries < config.number_of_retries) {
            return true;
        } else {
            return false;
        }
    };
    

    Metoda canRetry sprawdza liczbę ponownych prób, a następnie powraca true do kodu wywołującego, jeśli jest ok, aby ponowić próbę lub false czy aplikacja powinna teraz zostać zatrzymana.

  6. Teraz możemy użyć tej klasy, aby dodać zasady ponawiania prób do aplikacji do czatu.

Dodawanie zasad ponawiania

  1. Wybierz trzy wielokropki (...) w prawym górnym rogu edytora, a następnie wybierz pozycję Otwórz w oknie dialogowym wpisz server.js, a następnie naciśnij klawisz Enter.

  2. Przewiń w dół do wywołania platformy mongoose w celu połączenia z bazą danych.

    
    // Connect to MongoDB
    mongoose.connect( dbUrl, options )
      .then(() => console.log('Connection to MongoDB successful'))
      .catch(function(e) {
        console.log(e); // "error connecting to the database"
      });
    
    
  3. Dodaj kod, aby użyć zasad ponawiania wewnątrz obietnicy catch.

      .catch(function(e) {
        if (retries.checkRetries()) {
          // need to retry
        } else {
          console.log(e); // "error connecting to the database"
        }
      });
    
  4. Istnieje kilka sposobów w języku JavaScript, aby ponowić próbę kodu. Dla uproszczenia w tym przykładzie użyto rekursji. Zastąp kod połączenia poniższym kodem.

    // Connect to MongoDB
    function connectWithRetry() {
      mongoose.connect( dbUrl, options )
      .then(() => console.log('Connection to MongoDB successful'))
      .catch(function(e) {
        if (retries.checkRetries()) {
          connectWithRetry();
        } else {
          console.log(e); // "error connecting to the database"
        }
      });
    }
    
    // Using the retry policy
    connectWithRetry();
    

    Napiwek

    Istnieją inne pakiety npm ponawiania, w tym polly-js, które mogą uprościć kod i oferują dodatkowe funkcje, takie jak wycofywanie wykładnicze.

  5. Zdefiniuj obiekt ponownych prób i uwzględnij element retryPolicy.js w wierszu 6:

    // Include packages
    var express = require('express'), http = require('http');
    var bodyParser = require('body-parser')
    var mongoose = require('mongoose');
    var app = express();
    
    // Start node app listening
    var server = http.createServer(app);
    server.listen(8000);
    server.on('error', function (err) {
      console.log(err);
    })
    
    

    Dodając ten kod:

    // add the retry policy
    let retry = require('./retryPolicy.js');
    let retries = new retry();
    
  6. Zapisz plik i zamknij edytor. Użyj poleceń w menu ... w prawym górnym rogu edytora lub użyj klawiszy skrótu Ctrl+S, aby zapisać plik, i Ctrl+Q, aby zamknąć edytor.

  7. Jeśli przeglądarka nie jest otwarta w poprzednim ćwiczeniu, uruchom polecenie:

    curl -X POST http://localhost:8888/openPort/8000;
    
  8. Uruchom aplikację Node przy użyciu następującego kodu:

    npm build
    npm start
    
  9. Wybierz zwrócone hiperłącze z poprzedniego kroku.

  10. Dodaj komunikaty i odśwież stronę. Pozostaw uruchomioną aplikację.

Testowanie kodu ponawiania

Jeśli zapora jest nadal włączona dla usługi Azure Cosmos DB, nie można połączyć aplikacji do czatów z bazą danych. W przeciwnym razie jeśli aplikacja do czatów jest nadal uruchomiona, wykonaj następujące kroki, aby włączyć zaporę.

  1. Zaloguj się w witrynie Azure Portal przy użyciu tego samego konta, które zostało użyte do aktywowania piaskownicy.

  2. W menu witryny Azure Portal lub na stronie głównej wybierz pozycję Azure Cosmos DB.

  3. Na liście kont bazy danych wybierz konto bazy danych z nazwą rozpoczynającą się od learn-cosmos-db-.

  4. W panelu usługi Azure Cosmos DB wybierz pozycję Zapory i sieci wirtualne.

  5. W obszarze Zezwól na dostęp z wybierz opcję Wybrane sieci.

  6. Usuń zaznaczenie pola wyboru Zezwalaj na dostęp z witryny Azure Portal.

  7. Zaznacz pole wyboru Rozumiem, że bieżące ustawienia spowodują zablokowanie wszystkich sieci wirtualnych i adresów IP, w tym witrynę Azure Portal.

  8. Wybierz pozycję Zapisz, aby zapisać aktualizacje konfiguracji zapory. Te zmiany umożliwiły zaporę dla konta usługi Azure Cosmos DB, która blokuje dostęp z usługi Cloud Shell, symulując awarię połączenia.

    Uwaga

    Zastosowanie tych aktualizacji zapory może potrwać pewien czas, dlatego zaczekaj na ich zakończenie przed przejściem do następnego kroku.

  9. Uruchom aplikację i wybierz pozycję R , aby odświeżyć wszystkie komunikaty. Aplikacja ponownie przechwytuje System.TimeoutException element i ponawia próbę połączenia z bazą danych.

number-of-retries Na podstawie ustawienia w pliku appsettings.json naszego projektu kod ponawia próbę nawiązania połączenia do pięciu razy.

number_of_retries Na podstawie ustawienia w pliku config.properties naszego projektu kod ponawia próbę nawiązania połączenia do pięciu razy.

number-of-retries Na podstawie ustawienia w pliku appsettings.json naszego projektu kod ponawia próbę nawiązania połączenia do pięciu razy.

  1. Wróć do witryny Azure Portal, wybierz pozycję Wszystkie sieci, a następnie wybierz pozycję Zapisz , aby wyłączyć zaporę.

  2. Jeśli aplikacja zakończyła pracę, uruchom ją ponownie.

  3. Jeśli zapora zostanie usunięta w czasie, aplikacja czatu odzyskuje, ponownie nawiązuje połączenie i wyświetla przechowywane komunikaty.

Zmienianie zasad ponawiania

  1. Zamknij aplikację i zaktualizuj konfigurację zasad ponawiania tak, aby oczekiwać tylko 5 sekund (5000 milisekund) przed każdym ponowieniem próby.
  1. Otwórz plik konfiguracji w edytorze kodu.

    code appsettings.json
    
  2. Zmień opóźnienie z 60000 na 5000.

  3. Zapisz plik i zamknij edytor. Użyj poleceń w menu ... w prawym górnym rogu edytora lub użyj klawiszy skrótu Ctrl+S, aby zapisać plik, i Ctrl+Q, aby zamknąć edytor.

  4. Uruchom aplikację. Zauważ, że ponowne próby są podejmowane znacznie szybciej.

    dotnet run
    
  1. Otwórz plik konfiguracji w edytorze kodu.

    code config.properties
    
  2. Zmień opóźnienie z 60 na 5.

  3. Zapisz plik i zamknij edytor. Użyj poleceń w menu ... w prawym górnym rogu edytora lub użyj klawiszy skrótu Ctrl+S, aby zapisać plik, i Ctrl+Q, aby zamknąć edytor.

  4. Uruchom aplikację. Zauważ, że ponowne próby są podejmowane znacznie szybciej.

    java -cp .:lib/* learn.javachatapp.javaChat
    
  1. Otwórz plik konfiguracji w edytorze kodu.

    code appsettings.json
    
  2. Zmień opóźnienie z 60000 na 5000.

  3. Zapisz plik i zamknij edytor. Użyj poleceń w menu ... w prawym górnym rogu edytora lub użyj klawiszy skrótu Ctrl+S, aby zapisać plik, i Ctrl+Q, aby zamknąć edytor.

  4. Uruchom aplikację. Zauważ, że ponowne próby są podejmowane znacznie szybciej.

    npm start