Exercício – implementar resiliência de aplicativo
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:
- Instale todos os requisitos do sistema para executar o Contêiner de Desenvolvimento no Visual Studio Code.
- Confira se o Docker está em execução.
- Em uma nova janela do Visual Studio Code, abra a pasta do repositório clonado
- Pressione Ctrl+Shift+P para abrir a paleta de comandos.
- Pesquisa: >Contêineres de Desenvolvimento: Reconstruir e Reabrir no Contêiner
- Selecione eShopLite - dotnet-resiliency na lista suspenso. O Visual Studio Code cria seu contêiner de desenvolvimento localmente.
Compilar e executar o aplicativo
No painel inferior, selecione para a guia TERMINAL e execute o seguinte comando, vá para a raiz do código:
cd dotnet-resiliency
Execute o seguinte comando para compilar as imagens do aplicativo eShop:
dotnet publish /p:PublishProfile=DefaultContainer
Depois que a compilação for concluída, execute o seguinte comando para iniciar o aplicativo:
docker compose up
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
.O aplicativo eShop deve estar em execução. Selecione o item de menu Produtos à esquerda e você deverá ver a lista de produtos.
Testar a resiliência atual
Agora, você interromperá o serviço de produtos para ver o que acontece com o aplicativo.
Volte para o seu codespace e, na guia TERMINAL, selecione + para abrir um novo terminal bash.
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
Procure a ID do CONTÊINER referente ao contêiner productservice. No exemplo acima, a ID é 6ba80f3c7ab0.
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
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.
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
No seu codespace, na guia TERMINAL, navegue até a pasta Store do projeto:
cd Store
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.
Na barra lateral EXPLORADOR, selecione Program.cs.
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
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.
Execute o seguinte comando para recompilar o aplicativo eShop:
cd .. dotnet publish /p:PublishProfile=DefaultContainer
Quando a compilação for concluída, execute o seguinte comando para iniciar o aplicativo:
docker compose up
Retorne à guia do navegador que está executando o aplicativo e atualize a página de produtos. Você deverá ver a lista de produtos.
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.
Execute novamente o comando do docker stop:
docker stop <CONTAINER ID>
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.
Volte para o seu codespace e, na guia TERMINAL, selecione o terminal docker.
No terminal, pressione Ctrl+C para interromper a execução do aplicativo.
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.
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.
Pare o Docker com Ctrl+C. Em seguida, execute o seguinte comando para recompilar o aplicativo eShop:
dotnet publish /p:PublishProfile=DefaultContainer
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.
No terminal, pressione Ctrl+C para interromper o aplicativo.
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.
Execute o seguinte comando para recompilar o aplicativo eShop:
dotnet publish /p:PublishProfile=DefaultContainer
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.
No menu à esquerda, selecione o ícone Docker.
No painel DOCKER, em CONTÊINERES, clique com o botão direito do mouse no contêiner products e selecioneParar.
Retorne à guia do navegador que está executando o aplicativo e atualize a página de produtos. Você deverá ver a mensagem Carregando....
Volte para o seu codespace e, na guia TERMINAL, selecione o terminal docker. Observe que a estratégia de resiliência está funcionando.
No painel DOCKER, em CONTÊINERES, clique com o botão direito do mouse no contêiner productservice e selecioneIniciar.
Volte para a guia navegador que executa o aplicativo. Aguarde e o aplicativo deve se recuperar, mostrando a lista dos produtos.
No Terminal, interrompa o Docker com Ctrl+C.