Esercizio - Implementare la resilienza delle applicazioni

Completato

Il progetto eShop include due servizi che comunicano tra loro usando richieste HTTP. Il servizio Store chiama il servizio Product per ottenere l'elenco di tutti i prodotti correnti disponibili per l'acquisto.

La versione corrente dell'app non prevede alcuna gestione della resilienza. Se il servizio Product non è disponibile, il servizio Store restituisce un errore ai clienti e chiede di riprovare più tardi. Questo comportamento non è un'esperienza utente ottimale.

Il responsabile chiede di aggiungere resilienza all'app, in modo che il servizio Store ritenti la chiamata al servizio back-end in caso di errore.

In questo esercizio si aggiunge resilienza a un'app nativa del cloud esistente e si testa la correzione.

Aprire l'ambiente di sviluppo

Si può scegliere di ospitare l'esercizio in un codespace GitHub oppure di completarlo in locale in Visual Studio Code.

Per usare un codespace, creare un codespace GitHub preconfigurato da questo collegamento.

GitHub impiega qualche minuto per creare e configurare il codespace. Al termine del processo, vengono visualizzati i file di codice per l'esercizio. Il codice da usare per il resto di questo modulo si trova nella directory /dotnet-resiliency.

Per usare Visual Studio Code, clonare il repository https://github.com/MicrosoftDocs/mslearn-dotnet-cloudnative nel computer locale. Quindi:

  1. Installare i requisiti di sistema per eseguire il contenitore di sviluppo in Visual Studio Code.
  2. Verificare che Docker sia in esecuzione.
  3. In una nuova finestra di Visual Studio Code aprire la cartella del repository clonato
  4. Premere CTRL+MAIUSC+P per aprire il riquadro comandi.
  5. Ricerca: >Contenitori di sviluppo: Rebuild and Reopen in Container(Ricompilare e riaprire nel contenitore)
  6. Selezionare eShopLite - dotnet-resiliency dall'elenco a discesa. Visual Studio Code crea il contenitore di sviluppo in locale.

Compilare ed eseguire l'app

  1. Nel pannello inferiore selezionare la scheda TERMINALE ed eseguire il comando seguente alla radice del codice:

    cd dotnet-resiliency
    
  2. Eseguire il comando seguente per creare le immagini dell'app eShop:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. Al termine della compilazione, eseguire il comando seguente per avviare l'app:

    docker compose up
    
  4. Nel pannello inferiore selezionare la scheda PORTE e quindi nella colonna Indirizzo inoltrato della tabella selezionare l'icona Apri nel browser per la porta Front End (32000).

    Se si esegue l'app in locale, aprire una finestra del browser per visualizzare http://localhost:32000/products.

  5. L'app eShop dovrebbe essere in esecuzione. Selezionare la voce di menu Products. Verrà visualizzato l'elenco dei prodotti.

    Screenshot che mostra l'app eShop in esecuzione in un browser.

Testare la resilienza corrente

Arrestare il servizio dei prodotti per vedere cosa accade all'app.

  1. Tornare al codespace e nella scheda TERMINALE selezionare + per aprire un nuovo terminale bash.

  2. Eseguire il comando docker seguente per elencare i contenitori in esecuzione:

    docker ps
    

    Verrà visualizzato l'elenco dei contenitori attualmente in esecuzione, ad esempio:

    CONTAINER ID   IMAGE                                                                            COMMAND                  CREATED          STATUS          PORTS                                                        NAMES
    c08285e8aaa4   storeimage                                                                       "dotnet Store.dll"       8 minutes ago    Up 8 minutes    80/tcp, 443/tcp, 0.0.0.0:5902->8080/tcp, :::5902->8080/tcp   eshoplite-frontend-1
    6ba80f3c7ab0   productservice                                                                   "dotnet Products.dll"    8 minutes ago    Up 8 minutes    80/tcp, 443/tcp, 0.0.0.0:5200->8080/tcp, :::5200->8080/tcp   eshoplite-backend-1
    cd0c822a5222   vsc-eshoplite-958868d22c9851dd911b2423199bfc782861d1a8f7afac48e5096a1b7516082f   "/bin/sh -c 'echo Co…"   27 minutes ago   Up 27 minutes     
    
  3. Cercare l'ID CONTENITORE per il contenitore productservice. Nell'esempio precedente l'ID è 6ba80f3c7ab0.

  4. Arrestare il servizio dei prodotti con questo comando docker:

    docker stop <CONTAINER ID>
    

    Dove <CONTAINER ID> è l'ID trovato nel passaggio precedente. Ad esempio:

    docker stop 6ba80f3c7ab0
    
  5. Tornare alla scheda del browser in cui è in esecuzione l'app e aggiornare la pagina. Verrà visualizzato un messaggio di errore:

    There is a problem loading our products. Riprova più tardi.

  6. Tornare al codespace e nel TERMINALE selezionare il terminale docker e premere CTRL+C per arrestare l'app. Dovrebbe essere visualizzato quanto segue:

    Gracefully stopping... (press Ctrl+C again to force)
    Aborting on container exit...
    [+] Stopping 2/1
     ✔ Container eshoplite-frontend-1  Stopped                                                                      0.3s 
     ✔ Container eshoplite-backend-1   Stopped                                                                      0.0s 
    canceled
    

Aggiungere resilienza all'app

I primi passaggi per rendere l'app più resiliente prevedono l'aggiunta del pacchetto NuGet Microsoft.Extensions.Http.Resilience al progetto. È quindi possibile usarlo in Program.cs.

Aggiungere il pacchetto Microsoft.Extensions.Http.Resilience

  1. Nel codespace, nella scheda TERMINALE passare alla cartella del progetto Store:

    cd Store
    
  2. Eseguire il comando seguente per aggiungere il pacchetto NuGet per la resilienza:

    dotnet add package Microsoft.Extensions.Http.Resilience
    

    L'esecuzione di questo comando dal terminale nella cartella del progetto apps aggiunge il riferimento al pacchetto al file di progetto Store.csproj.

  3. Nella barra laterale ESPLORA RISORSE selezionare Program.cs.

  4. Aggiungere l'istruzione using seguente all'inizio del file:

    using Microsoft.Extensions.Http.Resilience;
    

Aggiungere una strategia di resilienza standard

  1. Nella riga 13, prima di ;, aggiungere questo codice:

    .AddStandardResilienceHandler()
    

    Il codice dovrebbe essere simile al seguente:

    builder.Services.AddHttpClient<ProductService>(c =>
    {
        var url = builder.Configuration["ProductEndpoint"] ?? throw new InvalidOperationException("ProductEndpoint is not set");
    
        c.BaseAddress = new(url);
    }).AddStandardResilienceHandler();
    

    Il codice precedente aggiunge un gestore di resilienza standard a HTTPClient. Il gestore usa tutte le impostazioni predefinite per la strategia di resilienza standard.

    Non sono necessarie altre modifiche al codice per l'app. A questo punto eseguiremo l'app e ne testeremo la resilienza.

  2. Eseguire i comandi seguenti per ricompilare l'app eShop:

    cd ..
    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. Al termine della compilazione, eseguire il comando seguente per avviare l'app:

    docker compose up
    
  4. Tornare alla scheda del browser in cui è in esecuzione l'app e aggiornare la pagina dei prodotti. Verrà visualizzato l'elenco dei prodotti.

  5. Tornare al codespace e nella scheda TERMINALE selezionare il secondo terminale bash. Copiare l'ID CONTENITORE per il contenitore productservice.

  6. Eseguire di nuovo il comando docker stop:

    docker stop <CONTAINER ID>
    
  7. Tornare alla scheda del browser in cui è in esecuzione l'app e aggiornare la pagina dei prodotti. Questa volta, l'operazione dovrebbe richiedere più tempo fino a quando non viene visualizzato il messaggio di errore delle app:

    There's a problem loading our products. Riprova più tardi.

    Controlleremo ora i log per verificare se la strategia di resilienza funziona.

  8. Tornare al codespace e nella scheda TERMINALE selezionare il terminale docker.

  9. Nel terminale premere CTRL+C per arrestare l'app in esecuzione.

  10. Nei messaggi di log scorrere verso l'alto fino a trovare i riferimenti a Polly.

    eshoplite-frontend-1  | warn: Polly[3]
    eshoplite-frontend-1  |       Execution attempt. Source: 'ProductService-standard//Standard-Retry', Operation Key: '', Result: 'Name or service not known (backend:8080)', Handled: 'True', Attempt: '2', Execution Time: '27.2703'
    

    Dovrebbero essere visualizzati molti messaggi come questo e ognuno corrisponde a un tentativo di ripetizione. Il messaggio precedente mostra il secondo tentativo e il tempo necessario per l'esecuzione.

Configurare una strategia di resilienza

Aggiungere resilienza a un'app significa trovare un equilibrio tra la necessità di rispondere rapidamente agli utenti e quella di non sovraccaricare alcun servizio back-end. Le opzioni predefinite potrebbero soddisfare o meno specifiche esigenze aziendali.

In questo esempio si vuole che il servizio Store attenda un po' più a lungo, per migliorare le possibilità di recupero.

  1. Nella finestra del codice per Program.cs modificare il codice alla riga 13 come segue:

    .AddStandardResilienceHandler(options =>
    {
        options.Retry.MaxRetryAttempts = 7;
    });
    

    Il codice precedente modifica le impostazioni predefinite della strategia di ripetizione dei tentativi in modo da impostare un massimo di sette tentativi. Tenere presente che la strategia è un backoff esponenziale, quindi il tempo totale è di circa 5 minuti.

  2. Arrestare docker con CTRL+C. Eseguire quindi il comando seguente per ricompilare l'app eShop:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. Al termine della compilazione, eseguire il comando seguente per avviare l'app:

    docker compose up
    

    Arrestare il contenitore del servizio back-end nel terminale bash e aggiornare eShop. Si noti che la visualizzazione del messaggio di errore richiede più tempo. Se si controllano però i log, è possibile notare che la strategia di ripetizione dei tentativi è stata ritentata solo cinque volte. L'ultimo messaggio di Polly è:

    Polly.Timeout.TimeoutRejectedException: The operation didn't complete within the allowed timeout of '00:00:30'.
    

    Il messaggio precedente indica che il timeout totale per le richieste impedisce il raggiungimento del numero massimo di tentativi. È possibile risolvere il problema aumentando il timeout totale per le richieste.

  4. Nel terminale premere CTRL+C per arrestare l'app.

  5. Nella finestra del codice per Program.cs modificare il codice alla riga 13 come segue:

    .AddStandardResilienceHandler(options =>
    {
        options.Retry.RetryCount = 7;
        options.TotalRequestTimeout = new HttpTimeoutStrategyOptions
        {
            Timeout = TimeSpan.FromMinutes(5)
        };
    });
    

    Il codice precedente modifica il timeout totale per le richieste impostandolo su 260 secondi, un valore ora più lungo della strategia di ripetizione dei tentativi.

    Con queste modifiche dovrebbe esserci tempo sufficiente per eseguire l'app, arrestare il servizio dei prodotti, controllare i tentativi di ripetizione nel log del terminale, aggiornare eShop per visualizzare il messaggio di caricamento e infine riavviare il servizio dei prodotti per visualizzare correttamente l'elenco dei prodotti.

  6. Eseguire il comando seguente per ricompilare l'app eShop:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  7. Al termine della compilazione, eseguire il comando seguente per avviare l'app:

    docker compose up
    

Testare le nuove opzioni di resilienza

Per testare l'app nel contenitore, usare l'estensione Docker. L'estensione offre un'interfaccia utente grafica per visualizzare e controllare lo stato dei contenitori.

  1. Dal menu a sinistra selezionare l'icona Docker.

    Screenshot dell'estensione Docker, che mostra come arrestare il servizio prodotti.

  2. Nel pannello DOCKER in CONTENITORI fare clic con il pulsante destro del mouse sul contenitore products e selezionare Arresta.

  3. Tornare alla scheda del browser in cui è in esecuzione l'app e aggiornare la pagina dei prodotti. Verrà visualizzato il messaggio Loading....

  4. Tornare al codespace e nella scheda TERMINALE selezionare il terminale docker. La strategia di resilienza funziona.

  5. Nel pannello DOCKER in CONTENITORI fare clic con il pulsante destro del mouse sul contenitore products e selezionare Avvia.

  6. Tornare alla scheda del browser in cui è in esecuzione l'app. Attendere. L'app verrà ripristinata e mostrerà l'elenco dei prodotti.

  7. Nel terminale arrestare docker con CTRL+C.