Czy Twój serwis jest zdrowy? Kontrole stanu w ASP.NET Core 6.0

Krzysztof Waśko

Aby utrzymać ogólną stabilność i wydajność systemu w świecie mikrousług i rozproszonych systemów, kluczowe jest monitorowanie stanu zdrowia każdej usługi. ASP.NET Core 6.0 ma wbudowane wsparcie dla kontroli stanu, umożliwiając deweloperom łatwe jej wdrożenie w aplikacjach. W tym artykule pokażę Ci, jak implementować kontrole stanu w ASP.NET Core 6.0. Wykorzystamy w tym celu zarówno wbudowane funkcje, jak i biblioteki stron trzecich, np. AspNetCore.Diagnostics.HealthChecks.

Wymagania wstępne

Aby śledzić przykłady w tym artykule, niezbędne są:

  1. Podstawowa wiedza o .NET 6.0
  2. Edytor tekstu do pisania kodu (np. Visual Studio Code)

Pierwszy krok

Aby zacząć, utwórz nowy projekt ASP.NET Core 6.0 Web API. Możesz to zrobić, używając następującego polecenia:

dotnet new webapi -n HealthCheckExample -f net6.0

Następnie przejdź do folderu HealtCheckExample i otwórz plik Program.cs. Zacznij od dodania middleware do kontroli stanu do aplikacji.

Dodawanie middleware do kontroli stanu

Aby dodać middleware do kontroli stanu, musisz zarejestrować usługę AddHealthChecks w metodzie ConfigureServices naszego pliku Program.cs. Zaktualizuj metodę Main, aby zawierała następujący kod:

using var builder = WebApplication.CreateBuilder(args);

// Add Health Checks
builder.Services.AddHealthChecks();

var app = builder.Build();

// Add Health Check endpoint
app.MapHealthChecks("/health");

app.MapControllers();

app.Run();

W powyższym kodzie dodaliśmy usługę kontroli stanu, wywołując AddHealthChecks() na właściwości Services buildera. Następnie utworzyliśmy punkt końcowy dla kontroli stanu pod ścieżką "/health", używając metody MapHealthChecks.

Testowanie podstawowej kontroli stanu

Gdy masz już middleware kontroli stanu i punkt końcowy, możesz uruchomić aplikację i przetestować kontrolę stanu.

  • Uruchom aplikację, używając polecenia:
dotnet run
  • Przejdź do <https://localhost:5001/health> w swojej przeglądarce internetowej lub użyj narzędzia takiego jak Postman, aby wysłać żądanie HTTP GET do tego punktu końcowego. Gdy to zrobisz, powinna pojawić się następująca odpowiedź:
Healthy

Domyślna kontrola stanu wskazuje, że nasza aplikacja jest zdrowa. Jednak ta podstawowa kontrola stanu nie dostarcza zbyt wielu informacji o rzeczywistym stanie naszej aplikacji. Aby monitorować konkretne komponenty naszej aplikacji, warto więc dodać kilka niestandardowych kontroli stanu.

Dodawanie niestandardowych kontroli stanu

Wyobraź sobie, że Twoja aplikacja polega na bazie danych i kolejce komunikatów. Z pewnością chcesz monitorować stan zdrowia obu tych komponentów. Aby było to realne, możesz stworzyć niestandardowe klasy kontroli stanu implementujące interfejs IHealthCheck.

  • Na początek stwórz niestandardową kontrolę stanu dla naszej bazy danych. Utwórz nowy plik o nazwie DatabaseHealthCheck.cs i dodaj do niego następujący kod:
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.HealthChecks;

public class DatabaseHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        // Replace with your actual database connection check logic
        bool isHealthy = true;

        if (isHealthy)
        {
            return Task.FromResult(HealthCheckResult.Healthy("Database is healthy"));
        }

        return Task.FromResult(HealthCheckResult.Unhealthy("Database is not healthy"));
    }
}
  • Następnie utwórz niestandardową kontrolę stanu dla naszej kolejki komunikatów. Utwórz nowy plik o nazwie MessageQueueHealthCheck.cs i dodaj do niego poniższy kod:
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.HealthChecks;

public class MessageQueueHealthCheck : IHealthCheck
{
  public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken =default)
  {
    // Replace with your actual message queue connection check logic
    bool isHealthy = true;
    if (isHealthy)
        {
            return Task.FromResult(HealthCheckResult.Healthy("Message queue is healthy"));
        }

        return Task.FromResult(HealthCheckResult.Unhealthy("Message queue is not healthy"));
    }
}
  • Po dodaniu niestandardowych kontroli stanu możesz zarejestrować je w metodzie ConfigureServices pliku Program.cs. Jak to zrobić? Zaktualizuj metodę Main, aby zawierała taki kod:
using var builder = WebApplication.CreateBuilder(args);

// Add Health Checks and Custom Health Checks
builder.Services.AddHealthChecks()
    .AddCheck<DatabaseHealthCheck>("Database")
    .AddCheck<MessageQueueHealthCheck>("MessageQueue");

var app = builder.Build();

// Add Health Check endpoint
app.MapHealthChecks("/health");

app.MapControllers();

app.Run();

Gotowe – dodaliśmy niestandardowe kontrole stanu, wywołując metodę AddCheck i przekazując typ naszej kontroli stanu oraz nazwę dla niej.

Modyfikacja odpowiedzi punktu końcowego

Możesz zdefiniować niestandardową metodę do formatowania wyników kontroli stanu w formacie JSON.

  • Aby było to możliwe, utwórz nowy plik o nazwie CustomHealthCheckResponseWriter.cs w katalogu swojego projektu. Jak to zrobić? Spójrz:
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Diagnostics.HealthChecks;

public static class CustomHealthCheckResponseWriter
{
    public static async Task WriteCustomHealthCheckResponse(HttpContext context, HealthReport report)
    {
        context.Response.ContentType = "application/json";

        var healthCheckResults = new Dictionary<string, object>
        {
            { "status", report.Status.ToString() },
            { "results", new Dictionary<string, object>() }
        };

        foreach (var entry in report.Entries)
        {
            var entryValues = new Dictionary<string, object>
            {
                { "status", entry.Value.Status.ToString() },
                { "description", entry.Value.Description },
                { "duration", entry.Value.Duration.ToString() }
            };

            ((Dictionary<string, object>)healthCheckResults["results"]).Add(entry.Key, entryValues);
        }

        await context.Response.WriteAsync(JsonSerializer.Serialize(healthCheckResults, new JsonSerializerOptions { WriteIndented = true }));
    }
}
  • Następnie zaktualizuj plik Program.cs, aby używał niestandardowego ResponseWriter z klasy CustomHealthCheckResponseWriter:
// ...

var app = builder.Build();

// Add Health Check endpoints
app.MapHealthChecks("/health", new HealthCheckOptions
{
    Predicate = _ => true,
    ResponseWriter = CustomHealthCheckResponseWriter.WriteCustomHealthCheckResponse
});

// ...

app.Run();
  • Uruchom ponownie aplikację i przejdź do https://localhost:5001/health w przeglądarce internetowej. Możesz też użyć narzędzia takiego jak Postman, aby wysłać żądanie HTTP GET do tego punktu końcowego.

Tym razem powinna pojawić się odpowiedź w formacie JSON zawierająca status każdej niestandardowej kontroli stanu. Zobacz:

{
  "status": "Healthy",
  "results": {
    "Database": {
      "status": "Healthy",
      "description": "Database is healthy",
      "duration": "00:00:00.0009610"
    },
    "MessageQueue": {
      "status": "Healthy",
      "description": "Message queue is healthy",
      "duration": "00:00:00.0009615"
    }
  }
}

Korzystanie z bibliotek zewnętrznych

Jeśli chcesz rozszerzyć funkcjonalność kontroli stanu w Twojej aplikacji, możesz użyć bibliotek stron trzecich, np. AspNetCore.Diagnostics.HealthChecks. Zapewnia ona dodatkowe implementacje kontroli stanu dla różnych komponentów takich jak m.in. bazy danych czy pamięci podręczne.

  • Aby użyć tej biblioteki, najpierw dodaj odpowiednie pakiety NuGet do projektu. Przykład? Jeśli chcesz dodać kontrole stanu dla SQL Server i Redis, konieczne jest zainstalowanie następujących pakietów:
dotnet add package AspNetCore.HealthChecks.SqlServer
dotnet add package AspNetCore.HealthChecks.Redis
  • Następnie zaktualizuj plik Program.cs, aby zawierał niezbędne instrukcje using i zarejestrował dodatkowe kontrole stanu. Jak to zrobić? Spójrz:
using HealthChecks.SqlServer;
using HealthChecks.Redis;

// ...

using var builder = WebApplication.CreateBuilder(args);

// Add Health Checks and Custom Health Checks
builder.Services.AddHealthChecks()
    .AddCheck<DatabaseHealthCheck>("Database")
    .AddCheck<MessageQueueHealthCheck>("MessageQueue")
    .AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), name: "SQL Server")
    .AddRedis(builder.Configuration.GetConnectionString("Redis"), name: "Redis");

// ...

Gdy uruchomisz aplikację i przejdziesz do punktu końcowego kontroli stanu, zobaczysz dodatkowe kontrole stanu w odpowiedzi.

Dodawanie interfejsu użytkownika kontroli stanu

Kolejną przydatną funkcją dostarczaną przez bibliotekę AspNetCore.Diagnostics.HealthChecks jest HealthCheck UI. Ten interfejs użytkownika pozwala na wizualizację stanu zdrowia Twoich usług w internetowym panelu kontrolnym. HealthCheck UI może agregować stan zdrowia wielu usług, co czyni go cennym narzędziem w architekturze mikrousług. Jak skonfigurować HeltCheck UI?

  • Aby użyć HealthCheck UI, musisz zainstalować następujące pakiety NuGet:
dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspNetCore.HealthChecks.UI.Client
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
  • Następnie zaktualizuj swój plik Program.cs i zarejestruj HealthCheck UI:
using HealthChecks.UI.Client;
// ...
builder.Services.AddHealthChecksUI(setupSettings: setup =>
    {
        setup.AddHealthCheckEndpoint("main", "/health");
    })
    .AddInMemoryStorage();
// ...
app.MapHealthChecks("/health", new HealthCheckOptions(){
    Predicate = _ => true,
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
// ....
app.MapHealthChecksUI(options =>
{
    options.UIPath = "/health-ui";
});
// ...

W powyższym kodzie dodaliśmy usługę HealthCheck UI, wywołując AddHealthChecksUI() na właściwości Services buildera. Określiliśmy także użycie pamięci wewnętrznej do przechowywania historii kontroli stanu, co jest przydatne do celów rozwojowych. Co dalej?

  • Uruchom ponownie aplikację i przejdź do https://localhost:5001/health-ui w przeglądarce internetowej. Powinien pojawić się panel HealthCheck UI wyświetlający stan zdrowia zarejestrowanych usług.

Podsumowanie

W tym artykule pokazałem, jak wdrożyć kontrole stanu w ASP.NET Core 6.0, korzystając zarówno ze wbudowanych funkcji, jak i bibliotek stron trzecich (np. AspNetCore.Diagnostics.HealthChecks). Dodając kontrole stanu do swojej aplikacji, możesz lepiej monitorować jej komponenty i zapewnić, że pozostanie stabilna i wydajna.

Źródła

https://learn.microsoft.com/pl-pl/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-6.0 https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami