Exercício – implementar resiliência de aplicativo

Concluído

O projeto eShop possui dois serviços que se comunicam entre si por meio de solicitações HTTP. O serviço Store chama o serviço Product para obter a lista de todos os produtos disponíveis para compra.

A versão atual do aplicativo não possui nenhum tratamento de resiliência. Se o serviço Product estiver indisponível, o serviço Store retorna um erro aos clientes, orientando-os a tentar novamente mais tarde. Esse comportamento não é uma boa experiência do usuário.

Seu gerente sol a você que adicione resiliência ao aplicativo, de modo que o serviço Store tente novamente a chamada de serviço de back-end em caso de falha.

Neste exercício, você adicionará resiliência a um aplicativo nativo de nuvem existente e testará se a correção funcionou.

Abrir o ambiente de desenvolvimento

Você pode optar por usar um codespace do GitHub que hospeda o exercício ou concluí-lo localmente no Visual Studio Code.

Para usar um codespace, crie um GitHub Codespace pré-configurado com este link de criação de Codespace.

O GitHub leva vários minutos para criar e configurar o codespace. Após o processo ser concluído, você verá os arquivos de código do exercício. O código que será usado no restante deste módulo está no diretório /dotnet-resiliency.

Para usar o Visual Studio Code, clone o repositório https://github.com/MicrosoftDocs/mslearn-dotnet-cloudnative em seu computador local. Em seguida:

  1. Instale todos os requisitos do sistema para executar o Contêiner de Desenvolvimento no Visual Studio Code.
  2. Confira se o Docker está em execução.
  3. Em uma nova janela do Visual Studio Code, abra a pasta do repositório clonado
  4. Pressione Ctrl+Shift+P para abrir a paleta de comandos.
  5. Pesquisa: >Contêineres de Desenvolvimento: Reconstruir e Reabrir no Contêiner
  6. Selecione eShopLite - dotnet-resiliency na lista suspenso. O Visual Studio Code cria seu contêiner de desenvolvimento localmente.

Compilar e executar o aplicativo

  1. No painel inferior, selecione para a guia TERMINAL e execute o seguinte comando, vá para a raiz do código:

    cd dotnet-resiliency
    
  2. Execute o seguinte comando para compilar as imagens do aplicativo eShop:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. Depois que a compilação for concluída, execute o seguinte comando para iniciar o aplicativo:

    docker compose up
    
  4. No painel inferior, selecione a guia PORTAS e, na coluna Endereço Encaminhado da tabela, selecione o ícone Abrir no Navegador para a porta Front-End (32000).

    Se você estiver executando o aplicativo localmente, abra uma janela do navegador para exibir http://localhost:32000/products.

  5. O aplicativo eShop deve estar em execução. Selecione o item de menu Produtos à esquerda e você deverá ver a lista de produtos.

    Captura de tela mostrando o aplicativo eShop em execução em um navegador.

Testar a resiliência atual

Agora, você interromperá o serviço de produtos para ver o que acontece com o aplicativo.

  1. Volte para o seu codespace e, na guia TERMINAL, selecione + para abrir um novo terminal bash.

  2. Execute o seguinte comando do Docker para listar os contêineres em execução:

    docker ps
    

    Você verá a lista de contêineres em execução no momento, por exemplo:

    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. Procure a ID do CONTÊINER referente ao contêiner productservice. No exemplo acima, a ID é 6ba80f3c7ab0.

  4. Interrompa o serviço de produtos com o seguinte comando docker:

    docker stop <CONTAINER ID>
    

    Onde <CONTAINER ID> é a ID que você encontrou na etapa anterior. Por exemplo:

    docker stop 6ba80f3c7ab0
    
  5. Volte para a guia navegador que está executando o aplicativo e atualize a página. Você deverá ver uma mensagem de erro:

    Há um problema ao carregar nossos produtos. Tente novamente mais tarde.

  6. Volte para o seu codespace e, no TERMINAL, selecione o terminal docker e pressione Ctrl+C para interromper o aplicativo. Você deverá ver:

    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
    

Adicionar resiliência ao aplicativo

As primeiras etapas para aumentar a resiliência do seu aplicativo incluem adicionar o pacote NuGet Microsoft.Extensions.Http.Resilience ao projeto. Em seguida, você pode usá-lo em Program.cs.

Adicionar o pacote Microsoft.Extensions.Http.Resilience

  1. No seu codespace, na guia TERMINAL, navegue até a pasta Store do projeto:

    cd Store
    
  2. Execute o seguinte comando para adicionar o pacote NuGet de resiliência:

    dotnet add package Microsoft.Extensions.Http.Resilience
    

    Executar este comando a partir do terminal na pasta do projeto de aplicativos adicionará a referência do pacote ao arquivo de projeto Store.csproj.

  3. Na barra lateral EXPLORADOR, selecione Program.cs.

  4. Na parte superior do arquivo, adicione a seguinte instrução using:

    using Microsoft.Extensions.Http.Resilience;
    

Adicionar uma estratégia de resiliência padrão

  1. Na Linha 13, antes de ;, adicione este código:

    .AddStandardResilienceHandler()
    

    Seu código deve ficar assim:

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

    O código acima adiciona um manipulador de resiliência padrão ao HTTPClient. O manipulador usa todas as configurações padrão para a estratégia de resiliência padrão.

    Nenhuma outra alteração de código é necessária para seu aplicativo. Vamos executar o aplicativo e testar a resiliência.

  2. Execute o seguinte comando para recompilar o aplicativo eShop:

    cd ..
    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. Quando a compilação for concluída, execute o seguinte comando para iniciar o aplicativo:

    docker compose up
    
  4. Retorne à guia do navegador que está executando o aplicativo e atualize a página de produtos. Você deverá ver a lista de produtos.

  5. Volte para o seu codespace e, na guia TERMINAL, selecione o segundo terminal bash. Copie a ID do CONTÊINER referente ao contêiner productservice.

  6. Execute novamente o comando do docker stop:

    docker stop <CONTAINER ID>
    
  7. Retorne à guia do navegador que está executando o aplicativo e atualize a página de produtos. Desta vez, pode demorar um pouco até você ver a mensagem de erro do aplicativo:

    Há um problema ao carregar nossos produtos. Tente novamente mais tarde.

    Vamos verificar os logs para ver se nossa estratégia de resiliência está funcionando.

  8. Volte para o seu codespace e, na guia TERMINAL, selecione o terminal docker.

  9. No terminal, pressione Ctrl+C para interromper a execução do aplicativo.

  10. Nas mensagens de log, role para cima até encontrar referências ao 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'
    

    Você deverá ver muitas mensagens como esta, cada uma representando uma nova tentativa de execução. A mensagem acima mostra a segunda tentativa e o tempo que levou para executar.

Configurar uma estratégia de resiliência

Quando você adiciona resiliência ao seu aplicativo, está equilibrando a necessidade de fornecer uma resposta rápida aos seus usuários com a importância de não sobrecarregar nenhum serviço back-end. Somente você pode determinar se as opções padrão atendem às necessidades do seu negócio.

Neste exemplo, você gostaria que o serviço da loja esperasse um pouco mais, para dar ao serviço a chance de se recuperar.

  1. Na janela de código do Program.cs, altere o código na linha 13 para:

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

    O código acima altera o padrão da estratégia de novas tentativas para ter um número máximo de sete tentativas. Lembre-se de que a estratégia é uma retirada exponencial, portanto, o tempo total é de cerca de cinco minutos.

  2. Pare o Docker com Ctrl+C. Em seguida, execute o seguinte comando para recompilar o aplicativo eShop:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  3. Quando a compilação for concluída, execute o seguinte comando para iniciar o aplicativo:

    docker compose up
    

    Interrompa o contêiner de serviço de back-end no terminal bash e atualize o eShop. Observe que leva mais tempo para ver a mensagem de erro. Se você verificar os logs, notará que a estratégia de novas tentativas só foi repetida cinco vezes. A última mensagem do Polly é:

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

    A mensagem acima informa que a solicitação excedeu o tempo limite total, impedindo que o número máximo de tentativas seja atingido. Para corrigir isso, aumente o tempo limite total da solicitação.

  4. No terminal, pressione Ctrl+C para interromper o aplicativo.

  5. Na janela de código do Program.cs, altere o código na linha 13 para:

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

    O código acima altera o tempo limite total da solicitação para 260 segundos, que agora é maior do que a estratégia de novas tentativas.

    Com essas alterações, agora há tempo suficiente para executar o aplicativo, parar o serviço de produtos, verificar os logs no terminal para novas tentativas, atualizar o eShop para visualizar a mensagem de carregamento e, finalmente, reiniciar o serviço de produtos para ver a lista de produtos com sucesso.

  6. Execute o seguinte comando para recompilar o aplicativo eShop:

    dotnet publish /p:PublishProfile=DefaultContainer
    
  7. Quando a compilação for concluída, execute o seguinte comando para iniciar o aplicativo:

    docker compose up
    

Testar as novas opções de resiliência

Para ajudar a testar o aplicativo no contêiner, use a extensão do Docker. A extensão fornece uma GUI para exibir e controlar o estado dos contêineres.

  1. No menu à esquerda, selecione o ícone Docker.

    Uma captura de tela da extensão do Docker, mostrando como interromper o serviço de produtos.

  2. No painel DOCKER, em CONTÊINERES, clique com o botão direito do mouse no contêiner products e selecioneParar.

  3. Retorne à guia do navegador que está executando o aplicativo e atualize a página de produtos. Você deverá ver a mensagem Carregando....

  4. Volte para o seu codespace e, na guia TERMINAL, selecione o terminal docker. Observe que a estratégia de resiliência está funcionando.

  5. No painel DOCKER, em CONTÊINERES, clique com o botão direito do mouse no contêiner productservice e selecioneIniciar.

  6. Volte para a guia navegador que executa o aplicativo. Aguarde e o aplicativo deve se recuperar, mostrando a lista dos produtos.

  7. No Terminal, interrompa o Docker com Ctrl+C.