Todos deseamos crear aplicaciones sólidas y seguras que puedan crecer de forma ilimitada y gestionar cualquier volumen de peticiones.

No obstante, resulta cada vez más complicado vigilar la función de nuestras aplicaciones debido a la creciente complejidad de los sistemas distribuidos y las arquitecturas de microservicios.

Contar con un sistema que ofrezca retroalimentación rápida acerca del estado de salud de tus aplicaciones es fundamental.

Es en ese lugar donde se llevan a cabo las verificaciones de salud.

Los health checks proporcionan una manera de monitorear y verificar el estado de varios componentes de una aplicación, incluyendo:

  • Databases
  • APIs
  • Caches
  • External services

¿Qué son los Health Checks?

Las verificaciones de salud (Health Checks) son un mecanismo proactivo para monitorear y verificar el estado y la disponibilidad de una aplicación en ASP.NET Core.

ASP.NET Core tiene soporte integrado para implementar verificaciones de salud.

A continuación podemos observar una configuración básica que registra los servicios de verificación de salud y agrega el HealthCheckMiddleware para responder en la URL especificada.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/health");

app.Run();

El Health Check retorna un valor de HealthStatus que refleja la condición del servicio.

Hay tres valores específicos de HealthStatus:

  • HealthStatus.Healthy
  • HealthStatus.Degraded
  • HealthStatus.Unhealthy

Es posible utilizar HealthStatus para representar los distintos estados de la aplicación.

Un ejemplo es si existe una degradación del rendimiento de una aplicación se puede devolver el valor HealthStatus.Degraded.

Incorporación de Health Checks Personalizados

Es posible crear verificaciones de salud personalizados implementando la interfaz IHealthCheck.

Por ejemplo, se puede desarrollar una verificación para asegurar que la base de datos SQL Server esté accesible.

Es crucial usar una consulta que se ejecute rápidamente en la base de datos, como SELECT 1.

A continuación, se muestra un ejemplo de implementación de una verificación de salud personalizada en la clase SqlHealthCheck:

using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Diagnostics.HealthChecks;

public class SqlHealthCheck : IHealthCheck
{
    private readonly string _connectionString;

    public SqlHealthCheck(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("Database");
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        try
        {
            using var sqlConnection = new SqlConnection(_connectionString);

            await sqlConnection.OpenAsync(cancellationToken);

            using var command = sqlConnection.CreateCommand();
            command.CommandText = "SELECT 1";

            await command.ExecuteScalarAsync(cancellationToken);

            return HealthCheckResult.Healthy();
        }
        catch(Exception ex)
        {
            return HealthCheckResult.Unhealthy(context.Registration.FailureStatus.ToString(),
                                               exception: ex);
        }
    }
}

Una vez se haya implementado la verificación de salud personalizada, es necesario registrarla.

La anterior llamada a AddHealthChecks ahora se convierte en:

builder.Services.AddHealthChecks()
    .AddCheck<SqlHealthCheck>("custom-sql-server-health-check", HealthStatus.Unhealthy);

Le estamos asignando un nombre personalizado y configurando el estado que debe utilizar como resultado de falla en HealthCheckContext.Registration.FailureStatus.

Una pregunta que debemos realizarnos es: ¿Es necesario implementar un Health Check personalizado para cada servicio externo con el que la aplicación deba establecer conexión?

Uso de Bibliotecas de Health Check Existentes

No es necesario implementar Health Checks personalizados a diestra y siniestra, existen librerías de Health Check que han sido desarrolladas para primero deberías comprobar si ya existe una biblioteca.

En el repositorio de AspNetCore.Diagnostics.HealthChecks, puedes encontrar una amplia colección de paquetes de Health Checks para servicios y bibliotecas frecuentemente utilizados.

Aquí puedes encontrar algunos ejemplos:

  • SQL Server – AspNetCore.HealthChecks.SqlServer
  • Postgres – AspNetCore.HealthChecks.Npgsql
  • Redis – AspNetCore.HealthChecks.Redis
  • RabbitMQ – AspNetCore.HealthChecks.RabbitMQ
  • AWS S3 – AspNetCore.HealthChecks.Aws.S3
  • SignalR – AspNetCore.HealthChecks.SignalR

A continuación, se muestra cómo añadir Health Checks para PostgreSQL y RabbitMQ:

builder.Services.AddHealthChecks()
    .AddCheck<SqlHealthCheck>("custom-sql-server-health-check", HealthStatus.Unhealthy);
    .AddNpgSql(pgConnectionString)
    .AddRabbitMQ(rabbitConnectionString)

Formateando la Respuesta del Health Check

Predeterminadamente el endpoint que devuelve el estado del Health Check retornará un valor de cadena que representa un HealthStatus. Como se muestra en la siguiente imagen:

Este formato no es lo ideal si en caso se tiene múltiples Health Checks para varios servicios, siguiendo nuestro ejemplo si conectamos con servicios como: RabbitMQ, SQL Server, PostgreSQL. Es probable que se requiera visualizar u obtener el estado de cada servicio. Otro detalle es que si alguno de los servicios está fallando la respuesta retornará Unhealthy y no se sabrá qué servicio está teniendo problemas.

Se puede solucionar lo anterior utilizando un ResponseWriter, afortunadamente ya existe uno en la biblioteca AspNetCore.HealthChecks.UI.Client.

Para lo anterior instalaremos el paquete de NuGet:

dotnet add package AspNetCore.HealthChecks.UI.Client --version 8.0.1

Una vez que se hayan hecho estos cambios, se presentará la respuesta del endpoint del Health Check de la siguiente manera:

En conclusión

El monitoreo de aplicaciones es importante para rastrear la disponibilidad, el uso de recursos y los cambios en el rendimiento de una aplicación. Los Health Checks pueden ser utilizados en escenarios de conmutación por error en despliegues en la nube, exponiendo verificaciones de salud de los servicios y desplegando instancias nuevas para atender las solicitudes en caso sea necesario.

Puedes encontrar el código fuente de los ejemplos en el siguiente enlace:

https://github.com/yovafree/healthchecks-aspnetcore

Por favor síguenos y dale me gusta: