[.NET Core] Implementando a interface IHealthCheck


Nas minhas aventuras pelos estudos no mundo da arquitetura de serviços distribuídos, eu sempre notei que autores enfatizam a necessidade de saber quando parar de “consumir” um serviço que está indisponível. Isso é saudável quando o motivo da indisponibilidade do serviço é justamente o excesso de requisições, não é? Neste artigo eu trago uma forma simples de implementar a interface IHealthCheck para que nossas API’s possam fornecer de uma forma rápida e objetiva os meios para que as aplicações client saibam quando algum recurso está indisponível. Eu aprendi sobre este recurso no último evento no qual eu estive presente aqui em Campinas, São Paulo. O evento foi o Microsoft Connect(); 2019 e contou com a presença de alguns MVPs de calibre da Micro<3soft.

Criando o projeto

Atualmente eu estou na versão 2.2.102 do .NET Core, mas provavelmente estes comandos irão continuar nas versões futuras.

Aqui eu invoquei o meu terminal e criei um diretório chamado healthcheck, naveguei para a raiz e rodei o comando que cria um projeto padrão do tipo webapi:

A ideia aqui é criar duas classes que implementam a interface IHealthCheck e simular uma cenário onde um dos recursos irá falhar e outro terá um retorno Ok. Com isso poderemos observar a flexibilidade e a facilidade deste recurso. Estou o Visual Studio Code como editor de código padrão. Com alguns plugins e pouca configuração, é possível ter todos os recursos que precisamos para poder “codar” C# em qualquer sistema operacional.

A classe CheckDatabaseHealthStatus

Na raiz do projeto eu criei uma nova classe com nome de CheckDatabaseHealthStatus e declarei que ela implementa a interface IHealthCheck.

No fim, o arquivo CheckDatabaseHealthStatus.cs deve ficar assim:

A variável healthStatus é onde vamos “jogar” todo o resultado das verificações que precisamos fazer para saber se este recurso específico está “em pé” ou não. Neste exemplo eu simplesmente setei ela para false, mas em uma aplicação real seria necessário a implementação de algo um pouco mais complexo, como uma consulta de testes ao banco de dados, por exemplo.

É na variável infoData onde vamos “anexar” as mais diversas informações que o usuário possa vir a precisar no momento da consulta. Ela segue um modelo de chave e valor que facilita bastante na hora de popular os valores.

A classe CheckWebserviceHealthStatus

Na raiz do projeto eu criei uma nova classe com o nome de CheckWebserviceHealthStatus e declarei que ela implementa a interface IHealthCheck.

No fim, o arquivoCheckWebserviceHealthStatus.cs deve ficar assim:

Para a CheckDatabaseHealthStatus, aproveitei algumas coisas da nossa primeira classe. O que realmente precisamos observar aqui, é onde eu causo uma exceção proposital para poder ilustrar o nosso exemplo. Dentro bloco catch, acesso a exceção via variável ex, capturo as informações úteis e as utilizo para popular as chaves na variável infoData.  Com a execução do programa desviada para o bloco catch, o retorno da função será um HealthCheckResult.Unhealthy, indicando que este recurso está com algum problema.

Até agora…

Bom, até agora nós criamos duas classes que implementam a interface IHealthCheck. O método CheckHealthAsync originado da interface, retorna um HealthCheckResult que atualmente implementa três opções de retorno:

  • HealthStatus.Healthy – Saudável “em pé, tudo ok, sucesso total”
  • HealthStatus.Degraded – Degradado “capenga, alta latência ou pouco recurso disponível”
  • HealthStatus.Unhealthy –  Indisponível “totalmente quebrada, fora do ar, offline”

Podemos variar os tipos retornados conforme a realidade da disponibilidade do recurso.

Registrando o HealthCheckService

Dentro do arquivo Startup.cs é onde vamos registrar o serviço para que o HealthCheck seja executado na aplicação. O método onde vamos registrar o serviço é o ConfigureServices.

Observe que as classes passadas como parâmetro no AddCheck  são as classes que implementamos anteriormente. As strings “Databases” e “Webservices” são apenas identificadores nominais que serão utilizados posteriormente. Poderíamos chamar de “cenoura” e “beterraba” que não teria problema algum.

Configurando o HealthCheckService

Ainda dentro do arquivo Startup.cs, vamos “dizer” para a nossa aplicação que ela deve usar o HealthCheckService e em qual URL ela irá responder com as informações dos status de cada recurso. O método a ser configurado  é o Configure:

O app.UseHealthChecks aceita dois parâmetros. O primeiro é uma string onde configuramos a rota para acessar as informações  e o segundo é um objeto do tipo HealthCheckOptions. O segundo não é obrigatório, mas é interessante para podermos customizar o retorno dos nossos status. Neste exemplo eu optei por implementar o segundo parâmetro, mas sem ele a declaração de uso é ainda mais simples:

O método WriteResponse também implementado no Startup.cs é responsável pelo nosso “output” personalizado. Reparem que eu atribuo o método WriteResponse para a propriedade ResponseWriter do segundo parâmetro e com isso nós ganhamos a capacidade de manipular o conteúdo retornado.

Implementar um ResponseWriter não é obrigatório, pois o HealthCheckService já entrega um resultado padrão de Healthy para resultados positivos e Unhealthy para negativos.

Check-list

Até agora temos:

  • Implementar a interface IHealthCheck nas classes de verificação
  • Registrar o serviço de HealthCheck no método ConfigureServices
  • Configurar o uso do HealthCheck com o app.UseHealthChecks no método Configure

Testando

O teste é a parte mais simples. O esperado aqui é que a aplicação devolva um status geral de falha, pois uma das nossas classes irá falhar propositalmente. O serviço de HealthCheck acumula os retornos dos métodos implementados em cada classe e devolve uma espécie de “catado lógico”, onde se pelo menos um único recurso está “unhealthy”, o “status geral” da aplicação toda encontra-se com problema. A vantagem é que ainda temos a informação sobre os recursos que estão “healthy”, permitindo que seja lá quem for consultar os status dos recursos, consiga identificar qual recurso ainda está em “pé” e desativar o consumo dos problemáticos, e manter ativo os recursos que estão em perfeito funcionamento.

Executei o servidor web embutido com o comando:

Acessei a URL http://localhost:5000/health no browser e obtive o seguinte resultado:

Notem a chave “status” de cada item da lista de resultados. Cada um manteve o seu status de forma individual, porém, como um deles falhou, o status geral retornado pela “página” foi de falha.

Http Codes

Um detalhe bem importante é considerar o Status Code de retorno do cabeçalho Http. Aplicações de modelo Restful dão (deveriam dar) bastante importância para os códigos retornados nas transações efetuadas. Em caso de falha geral, o serviço HealthCheck altera o cabeçalho do response e devolve com o código 503 – Service Unavailable e quando todos os recursos estão ok, naturalmente o retorno é o código 200 – OK.

Print da minha aba de “debug” do navegador

Fim e referências

Bom pessoal, é isso. Aqui está uma forma simples e objetiva de publicarmos os status dos recursos disponíveis em nossa API.

Para escrever este humilde artigo eu busquei referências em alguns sites. Resolvi deixar as fontes originais aqui caso eu não tenha sido muito claro em alguma parte específica.

Documentação oficial do HealthCheck:

https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-2.2

Criando um objeto anônimo em C#

https://docs.microsoft.com/pt-br/dotnet/csharp/programming-guide/classes-and-structs/anonymous-types

Tabela com os Http Codes

https://httpstatuses.com/

Projeto avançado de verificação de HealthChek

https://github.com/Xabaril/BeatPulse

GitHub com os fontes criados

https://github.com/thalesreis/healthcheck_demo

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *