Ćwiczenie — implementowanie zasad ponawiania w aplikacji

Ukończone

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

Jeśli błąd jest spowodowany problemami z połączeniem z bazą danych, przyjmiemy 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 CloudShell, 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;
            }
        }
    }
    

    Po sprawdzeniu liczby ponownych prób metoda CanRetry() zwraca wartość true do kodu wywołującego, jeśli można podejmować dalsze próby, lub wartość false, gdy aplikacja powinna od razu zatrzymać podejmowanie ponownych prób.

    Teraz użyjemy tej klasy w naszej aplikacji w celu wdrożenia zasad ponawiania.

Dodawanie zasad ponawiania

  1. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Otwórz plik... Wybierz plik Program.cs w oknie dialogowym wyboru plików, a następnie 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;
        }
    }
    

    Po sprawdzeniu liczby ponownych prób metoda canRetry zwróci wartość true do kodu wywołującego, jeśli można podejmować dalsze próby, lub wartość false, gdy powinna od razu zakończyć pracę.

  5. Teraz użyjesz tej klasy w celu dodania zasad ponawiania do aplikacji do czatów.

Dodawanie zasad ponawiania

  1. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Otwórz, w oknie dialogowym wpisz javaChat.java i 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. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Zamknij w oknie i kliknij pozycję Zapisz.

  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 środowiska Node.

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

    Po sprawdzeniu liczby ponownych prób metoda checkRetries zwróci wartość true do kodu wywołującego, jeśli można podejmować dalsze próby, lub wartość false, gdy powinna od razu zakończyć pracę.

  6. Teraz użyjesz tego kodu w celu dodania zasad ponawiania do aplikacji do czatów.

Dodawanie zasad ponawiania

  1. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Otwórz, w oknie dialogowym wpisz server.js i 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. W języku JavaScript można skorzystać z szeregu sposobów, aby ponowić próbę wykonania kodu. Dla uproszczenia w tym przykładzie używamy 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();
    

    Porada

    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. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Zamknij w oknie i kliknij pozycję Zapisz.

  7. Jeśli nie masz otwartej przeglądarki z poprzedniego ćwiczenia, uruchom:

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

    npm build
    npm start
    
  9. Kliknij hiperlink zwrócony 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 nadal działa, 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 włączyły zaporę dla konta usługi Azure Cosmos DB, co spowoduje zablokowanie dostępu z usługi Cloud Shell przez symulowanie awarii 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 R, aby odświeżyć wszystkie komunikaty. Aplikacja przechwyci element System.TimeoutException i jeszcze raz ponów próbę połączenia z bazą danych.

W oparciu o ustawienie number-of-retries w pliku appsettings.json naszego projektu kod spróbuje ponowić operację łączenia maksymalnie pięć razy.

W oparciu o ustawienie number_of_retries w pliku config.properties naszego projektu kod spróbuje ponowić operację łączenia maksymalnie pięć razy.

W oparciu o ustawienie number-of-retries w pliku appsettings.json naszego projektu kod spróbuje ponowić operację łączenia maksymalnie pięć razy.

  1. Po powrocie do witryny Azure Portal kliknij pozycję Wszystkie sieci, a następnie kliknij pozycję Zapisz, aby wyłączyć zapory.

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

  3. Jeśli zapora została usunięta w odpowiednim czasie, aplikacja do czatów odzyska sprawność, ponownie nawiąże połączenie i wyświetli 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. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Zamknij w oknie i kliknij pozycję Zapisz.

  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. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Zamknij w oknie i kliknij pozycję Zapisz.

  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. Wybierz trzy wielokropki (...) u góry po prawej stronie edytora, a następnie wybierz pozycję Zamknij w oknie i kliknij pozycję Zapisz.

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

    npm start