Exercício - Implementar resiliência de aplicativos

Concluído

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

A versão atual do aplicativo não tem manipulação de resiliência. Se o serviço Product não estiver disponível, o serviço Store devolve um erro aos clientes e pede-lhes para tentarem novamente mais tarde. Esse comportamento não é uma boa experiência do usuário.

Seu gerente solicita que você adicione resiliência ao aplicativo, para que o serviço Store tente novamente a chamada de serviço de back-end se ela falhar.

Neste exercício, você adiciona resiliência a um aplicativo nativo da nuvem existente e testa sua correção.

Abra o ambiente de desenvolvimento

Você pode optar por usar um espaço de código GitHub que hospeda o exercício ou concluí-lo localmente no Visual Studio Code.

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

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

Para usar Visual Studio Code, clone o repositório https://github.com/MicrosoftDocs/mslearn-dotnet-cloudnative para sua máquina local. Em seguida:

  1. Instale qualquer requisitos do sistema para executar o Dev Container no Visual Studio Code.
  2. Verifique 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: >Dev Containers: Reconstruir e Reabrir no Contêiner
  6. Selecione eShopLite - dotnet-resiliency na lista suspensa. O Visual Studio Code cria seu contêiner de desenvolvimento localmente.

Criar e executar o aplicativo

  1. No painel inferior, selecione o separador TERMINAL e execute o seguinte comando para aceder à raiz do código:

    cd dotnet-resiliency
    
  2. Execute o seguinte comando para criar as imagens do 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
    
  4. No painel inferior, selecione a guia PORTAS e, em seguida, na coluna Endereço Encaminhado da tabela, selecione o ícone Abrir no Navegador para a porta Front-End (32000).

    Se estiver a executar a aplicação localmente, abra uma janela do browser para ver http://localhost:32000/products.

  5. A aplicação eShop deve estar em execução. Selecione o Produtos item de menu, você verá a lista de produtos.

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

Testar a resiliência atual

Pare o serviço do produto para ver o que acontece com a aplicação.

  1. Volte ao seu espaço de código e, na guia TERMINAL, selecione + para abrir um novo terminal bash.

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

    docker ps
    

    Você deve 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 o ID do CONTAINER para o contêiner productservice. No exemplo acima, o ID é 6ba80f3c7ab0.

  4. Pare o serviço do produto com este comando Docker:

    docker stop <CONTAINER ID>
    

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

    docker stop 6ba80f3c7ab0
    
  5. Volte para a guia do navegador que executa o aplicativo e atualize a página. Deverá ver uma mensagem de erro:

    Existe um problema ao carregar os nossos produtos. Tente novamente mais tarde.

  6. Volte para o seu codespace e, no TERMINAL selecione o docker terminal e pressione Ctrl+C para parar o aplicativo. Você deve 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 tornar seu aplicativo mais resiliente são adicionar o pacote NuGet Microsoft.Extensions.Http.Resilience ao projeto. Você pode usá-lo em Program.cs.

Adicione o pacote Microsoft.Extensions.Http.Resilience

  1. No seu espaço de código, na guia TERMINAL, navegue até a pasta do projeto Store:

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

    dotnet add package Microsoft.Extensions.Http.Resilience
    

    A execução deste comando a partir do terminal na pasta do projeto de apps adiciona a referência do pacote ao arquivo de projeto Store.csproj.

  3. Na barra lateral do EXPLORER, selecione Program.cs.

  4. No topo do ficheiro, 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 ;, adicione este código:

    .AddStandardResilienceHandler()
    

    Seu código deve ter esta aparência:

    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 os seguintes comandos para reconstruir 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. Volte para a guia do navegador que executa o aplicativo e atualize a página do produto. Você deve ver a lista de produtos.

  5. Volte ao seu codespace e, na guia TERMINAL, selecione o segundo terminal bash. Copie o ID do CONTAINER para o contentor productservice.

  6. Execute novamente o comando docker stop:

    docker stop <CONTAINER ID>
    
  7. Volte para a guia do navegador que executa o aplicativo e atualize a página do produto. Desta vez, deve demorar um pouco mais até ver a mensagem de erro das aplicações:

    Há um problema ao carregar os 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 do docker.

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

  10. Nas mensagens de registo, desloque-se para cima até encontrar referências 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'
    

    Você deve ver muitas mensagens como esta; cada uma é uma tentativa de nova tentativa. A mensagem acima mostra a segunda tentativa e o tempo que levou para ser executada.

Configurar uma estratégia de resiliência

Ao adicionar resiliência ao seu aplicativo, você está equilibrando a necessidade de responder rapidamente aos usuários com a necessidade de não sobrecarregar nenhum serviço de back-end. Somente você pode decidir se as opções padrão atendem às necessidades da sua empresa.

Neste exemplo, você gostaria que o serviço da loja esperasse um pouco mais, para dar ao serviço da loja uma chance de recuperação.

  1. Na janela de código para 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 repetição para ter um número máximo de aposentados para sete. Lembre-se que a estratégia é um recuo exponencial, então o tempo total é de cerca de 5 minutos.

  2. Pare o Docker com Ctrl+C. Em seguida, execute o seguinte comando para reconstruir 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
    

    Pare o contêiner de serviço de back-end no terminal bash e atualize a eShop. Observe que leva mais tempo para ver a mensagem de erro. Se você verificar os registros, porém, poderá ver que a estratégia de repetição só foi repetida cinco vezes. A última mensagem de Polly é:

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

    A mensagem acima informa que o tempo limite total da solicitação impede que o número máximo de novas tentativas seja atingido. Você pode corrigir o problema aumentando o tempo limite total da solicitação.

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

  5. Na janela de código para 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 é mais longo do que a estratégia de repetição.

    Com essas alterações, você deve ter tempo suficiente para executar o aplicativo, parar o serviço do produto, verificar os logs do terminal para tentativas de novas tentativas, atualizar a eShop para ver a mensagem de carregamento e, finalmente, reiniciar o serviço do produto para ver com êxito a lista de produtos.

  6. Execute o seguinte comando para reconstruir 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 em seu contêiner, use a extensão Docker. A extensão fornece uma GUI para visualizar e controlar o estado dos contêineres.

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

    Uma captura de tela da extensão docker, mostrando como parar o serviço de produtos.

  2. No painel DOCKER, em CONTAINERS, clique com o botão direito do rato no contentor de produtos e selecione Parar.

  3. Volte para a guia do navegador que executa o aplicativo e atualize a página do produto. Deverá ver a mensagem Carregando....

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

  5. No painel DOCKER, em CONTÊINERS, clique com o botão direito do mouse no contêiner produtos e selecione Iniciar.

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

  7. No Terminal, pare o docker com Ctrl+C.