ASP.NET–Comprueba la disponibilidad de tus servicios

Si desarrollas una aplicación en ASP.NET y/o ASP.NET Core, te puede interesar una nueva librería que ha sacado el equipo de .NET: HealthChecks. Esa librería (muy sencilla) contiene lo que necesitas para poder validar que un determinado recurso externo (SQL Server, API remota, etc) está funcionando y también para que decidas lo que significa que un recurso “está funcionando”.

Actualmente la librería está en un estado “alfa” por decirlo de algún modo, así que no hay paquete NuGet todavía (ni readme.md xD) pero es muy sencilla de entender. De todos modos veamos un ejemplo muy sencillo. Para ello vamos a crear una solución (asp.net core) con dos proyectos:

  1. Una aplicación MVC Core que contiene el frontend
  2. Una aplicación MVC Core que nos representa una API externa y que usa un SQL Server.

El objetivo es el siguiente:

  • Que la aplicación web tenga una página para ver el estado de los servicios externos (en este caso la API)
  • La API funciona si es accesible via HTTP y además si el SQL está levantado.

Veamos como esa librería nos puede ayudar a construir ese ejemplo. Lo primero es crear la solución con dos aplicaciones MVC Core. En mi caso he creado una solución con dos proyectos MVC Core (WebClient  usando la plantilla de aplicación web y Api usando la plantailla de aplicación webapi):

image

Ahora toca agregar la librería. En algun momento, esto será añadir un paquete NuGet (quizá más de uno) pero por ahora es “manual”. Descárgate el código del repo y copia las siguientes carpetas en la solución:

  • src/common
  • src/Microsoft.AspNet.HealthChecks
  • src/Microsoft.Extensions.HealthChecks
  • src/Microsoft.Extensions.HealthChecks.Data

Ahora añade a la solución los proyectos que están en cada una de las carpetas añadidas (menos en common que no hay proyecto):

image

Ya lo tenemos todo a punto. Ahora vamos a modificar la aplicación web para que nos muestre un informe del estado de los servicios externos. Por el momento el único servicio externo es la aplicación Api.

Para ello, lo primero es agregar una referencia desde el proyecto WebClient al proyecto Microsoft.AspNetCore.HealthChecks:

Ahora en el método ConfigureServices de la clase Startup del proyecto WebClient agregamos el siguiente código:

services.AddHealthChecks(checks =>
{
    checks.AddUrlCheck(Configuration["check_api_url"]);
});

Con eso creamos un check de url. Este tipo de checks simplemente comprueban que una determinada URL devuelve un 200. Por supuesto necesitamos indicar (en este caso en el fichero de config) la url a usar:

"check_api_url": "http://localhost:56661/api/ping"

¿Sencillo, verdad? Simplemente establecemos que si /api/ping devuelve un 200, entonces significa que la API está levantada y funcionando.

Ahora creamos una acción en el controlador Home de nuestra WebClient que vamos a llamar Status. Dentro de esta acción usaremos un servicio proporcionado por la librería, para ejecutar los checks de forma manual:

public class HomeController : Controller
{
    private readonly IHealthCheckService _healthCheckSvc;
    public HomeController(IHealthCheckService healthSvc) => _healthCheckSvc = healthSvc;

    public async Task<IActionResult> Status()
    {
        var result = await _healthCheckSvc.CheckHealthAsync();
        return View(result);
    }

    public IActionResult Index()
    {
        return View();
    }


    public IActionResult Error()
    {
        return View();
    }
}

Observa como inyectamos el servicio IHeralthCheckService y llamamos al método CheckHealthAsync. Eso ejecutará todos los checks y nos devolverá un resultado para cada uno de ellos.

Ahora solo nos queda crear el endpoint de “ping” en el proyecto Api (en este caso devolvemos siempre un 200):

[Route("api/[controller]")]
public class PingController : Controller
{
    // GET api/values
    [HttpGet]
    public IActionResult Ping()
    {
        return StatusCode(200);
    }   
}

Si ahora colocamos un breakpoint en la acción Status del controlador Home de la web e inspeccionamos la variable result veremos que nos indica que el estado global del sistema es saludable y luego nos lo desgrana por cada uno de los checks:

image

Tampoco es para tanto…

Sí, seguro que es lo que estás pensando ¿verdad? Hay dos cosillas que debemos considerar sobre lo que hemos visto:

  1. Hemos tenido que crear un endpoint específico (/api/ping) en nuestra API
  2. ¿Qué ocurre con recursos externos, tales como BBDD u otros?

Veamos que soluciones nos ofrece la librería para esos dos puntos 😉

Middleware de estado de salud

Una cosa interesante de la librería es que ofrece un endpoint para comprobar el estado de salud de una api (es decir el equivalente al /api/ping que hemos hecho). Vamos a modificar nuestra Api para usar este middleware en lugar del endpoint /api/ping que hemos creado. Para ello podemos eliminar el PingController y luego debemos agregar referencias desde la Api hacia los proyectos Microsoft.AspNetCore.HealthChecks y  Microsoft.Extensions.HealthChecks.

Una vez hecho esto añadimos una llamada al método de extensión UseHealthChecks en el método main de la clase Program. Justo después del UseKestrel.

Este método espera un puerto (o un path). Este puerto (o path) será el endpoint a usar para comprobar el estado del sistema. P. ej. en mi caso he usado el puerto 5050. Por lo tanto si ahora me connecto a http://localhost:5050 (con la API en marcha, claro) veré una página que me informa del estado:

image

Nota: Si en lugar de usar un puerto prefieres usar un path para comprobar el estado, también se puede hacer. En este caso debes llamar a UseHealthChecks pasando el path a usar (p. ej. UseHealthChecks(“/status”) ).

Nota 2: Si usas un puerto (como he hecho yo), no uses IISExpress para ejecutar los proyectos, porque no tendrás el binding a ese nuevo puerto configurado en IISExpress. Si usas IISExpress usa un path, mejor. Si quieres usar un puerto, levanta Kestrel directamente con dotnet run.

Nos dice “Unknown” (además devuelve un HTTP 503) porque el middleware no sabe el estado del sistema actual (la API) porque no hemos metido ningún check que indique si la API está en buen estado. Vamos a suponer que queremos indicar que la API está en buen estado.

Para ello agregamos lo siguiente en el método ConfigureServices de la API:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecks(checks =>
    {
        checks.AddValueTaskCheck("AlwaysOk", () => new ValueTask<IHealthCheckResult>(HealthCheckResult.Healthy("Always OK", null)));
    });
    // Add framework services.
    services.AddMvc();
}

Este es un check que siempre devuelve el estado de Healthy.

Si ahora navegamos de nuevo al endpoint de verificación (localhost:5050 en nuestro caso) veremos lo siguiente:

image

¡Fantástico! Ahora la API nos reporta que está en buen estado (y devuelve un HTTP 200). Observa que ahora localhost:5050 es el endpoint de verificación que vamos a usar en la web, en lugar del api/ping.

En resúmen: el middleware de verificación nos ofrece un endpoint que al ser llamado ejecuta todos los checks definidos y devuelve el estado de salud del sistema en base a estos checks.

Así que ahora tenemos:

  1. La Web que tiene un check por url (al endpoint de verificación de la API)
  2. La Api que, usando el middleware de verificación, devuelve su estado (actualmente siempre OK).

Comprobando la disponibilidad de recursos externos

Comprobar la disponibilidad de recursos externos es, simplemente, usar checks específicos. En nuestro caso teníamos que la API dependía de un SQL Server. Observa que no debemos añadir un check para verificar el SQL Server en la web (no tiene sentido ya que la web no tiene porque conocer ni tener acceso a un recurso usado por la API). No, el check debemos agregarlo en la API. De esta manera si la BBDD está caída, el endpoint de verificación de la API indicará que el estado de la API es incorrecto.

Es de esperar que se agreguen varios checks a la librería en un futuro, pero de momento ya tenemos uno para verificar un SQL Server. Para ello debemos añadir una referencia al proyecto Microsoft.Extensions.HealthChecks.Data.

Ahora tenemos disponible un nuevo check que podemos agregar con AddSqlCheck. Así en el método ConfigureServices ahora tendremos:

services.AddHealthChecks(checks =>
{
    checks.AddSqlCheck("Main_SQL", Configuration["constr"]);
});

(Observa que hemos podido quitar el check anterior que siempre devolvía Ok. Ahora si la BBDD funciona, la API está OK y si no, no).

En resúmen

Esta librería nos permite verificar el estado de distintas APIs (y que cada API determine lo que significa que su estado es correcto) de una forma sencilla y extendible. Esto permite informar fácilmente al usuario del estado de los distintos componentes que forman la aplicación y, por supuesto, tomar decisiones en base a estos estados.

Espero que te haya resultado interesante y si quieres… anímate que están esperando feedback!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *