Реализация проверок здоровья PT.1 — конфигурация Asp.Net Core 6

  1. Внедрение проверок здоровья PT.1 — конфигурация Asp.Net Core 6 (этот пост)
  2. Реализация проверок работоспособности PT.2 — конфигурация Azure Application Insights

Когда работающее приложение недоступно для клиентов из-за технических проблем, мы не только теряем деньги, но и теряем доверие и веру наших клиентов. Фактически, они могут распространить плохое мнение о нашем продукте через социальные сети, друзей, отзывы в магазинах или — что еще хуже — перейти к конкурентам.

По сути, когда приложение недоступно для заинтересованных сторон, оно как будто не существует.

Конечно, такое может случиться. Это может произойти из-за человеческой ошибки, ошибочной конфигурации или бага. А может быть, потому что у нашего облачного провайдера возникли проблемы с подключением.

Мониторинг доступности приложения также позволяет разработать некоторые метрики для определения SLA вашего сервиса.

По этим причинам мы должны немедленно узнать, когда приложение переходит в автономный режим работы.

Одним из наиболее используемых и простых методов является проверка работоспособности конечной точки.
Конечная точка проверки работоспособности — это API, открываемый приложением, который не имеет никакой логики, кроме ответа с положительным кодом состояния; если вызывающая сторона не получает никакого ответа или код состояния не соответствует ожидаемому, это означает, что произошла ошибка и необходимо отправить предупреждение с помощью выбранной вами технологии (электронная почта, push-уведомление. SMS и т.д.).

Конечно, Asp.Net Core предоставляет встроенный механизм для реализации конечной точки проверки работоспособности.

Реализация простой конечной точки проверки состояния здоровья

Создайте проект Asp.Net Core. Для этого демо (исходный код вы можете найти в конце этой заметки) я буду использовать dotnet 6 с новым шаблоном минимального запуска хоста.

Установите NuGet-пакет Microsoft.Extensions.Diagnostics.HealthChecks и настройте его:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/alive");

app.Run();
Войти в полноэкранный режим Выйти из полноэкранного режима

Запустите приложение и перейдите на localhost:<port>/alive и вы должны увидеть что-то вроде этого:

Если вы попытаетесь вызвать ту же конечную точку с помощью Postman или аналогичного инструмента, вы увидите код состояния 200 OK.

Вот и все — конечная точка проверки здоровья готова к регулярному потреблению приложением watch dog 🎉.

Расширенный сценарий

Возможен сценарий, когда приложение работает в режиме онлайн, но по какой-то причине не может связаться с базой данных. Потенциально это большой недостаток для клиентов, и, скорее всего, пользователи не смогут выполнить почти все действия.

Проверка работоспособности Entity Framework

Также Entity Framework предоставляет встроенный механизм для мониторинга связи между приложением и базой данных экземпляра SQL.

NB: чтобы приступить к работе, конечно, необходимо, чтобы Entity Framework был настроен и указывал на работающую базу данных SQL

Установите пакет Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore из NuGet и настройте его:

services.
AddHealthChecks()
.AddDbContextCheck<MyDbContext>("/dbcontext");
Войдите в полноэкранный режим Выход из полноэкранного режима

Запустив приложение и снова указав на localhost:<port>/alive, вы должны получить тот же результат:

По умолчанию, под капотом проверяется:

DbContextHealthCheck вызывает метод EF Core’s CanConnectAsync. Вы можете настроить, какая операция будет выполняться при проверке здоровья, используя перегрузки метода AddDbContextCheck.
Имя проверки здоровья — это имя типа TContext.

Теперь это работает, но остается проблема: независимо от того, к какой конечной точке проверки здоровья мы обращаемся (/alive или /dbcontext), выполняются обе проверки, поэтому раскрытие двух разных API бесполезно.

Как можно задать разные конечные точки для разных проверок? Ответ: через имена проверок здоровья.

Измените регистрацию конечных точек следующим образом:

const string ALIVE = "alive";
const string ALIVE_DBCONTEXT = "dbcontext";

...
builder.Services.AddHealthChecks().AddDbContextCheck<MyDbContext>(name: ALIVE_DBCONTEXT);

app.MapHealthChecks(
    "/alive",
    new HealthCheckOptions { Predicate = (c) => c.Name == ALIVE });

app.MapHealthChecks(
    "/dbcontext",
    new HealthCheckOptions { Predicate = (c) => c.Name == ALIVE_DBCONTEXT });
Вход в полноэкранный режим Выход из полноэкранного режима

В этом случае мы даем имя определенной конечной точке проверки здоровья. Когда имя совпадает, выполняется только определенная проверка здоровья.

Чтобы получить подтверждение, мы можем провести тест:

  • запустите приложение и базу данных в локальном контейнере Docker
  • перейдите по адресу localhost:<port>/alive: вы должны получить Healthy в качестве результата
  • остановите контейнер базы данных и обновите страницу: вы все еще должны получить Healthy в качестве результата
  • перейдите по адресу localhost:<port>/dbcontext: вы должны получить Unhealthy в качестве результата
  • перезапустите контейнер базы данных и обновите страницу: в результате вы должны получить Healthy.

Пользовательские проверки: Azure Cosmos DB

Asp.Net позволяет определить некоторый пользовательский код при выполнении проверки работоспособности. Это тот случай, когда мы можем проверить наше соединение с экземпляром Azure Cosmos DB, поскольку .NET SDK не предоставляет никакого механизма для проверки соединения между приложением и службой базы данных.

Следующий пример тестирует соединение, используя container.ReadContainerAsync(), метод, открытый Cosmos SDK, для выполнения которого необходим доступ к базе данных.

// Cosmos initialization

builder.Services
    .AddHealthChecks()
    .AddDbContextCheck<MyDbContext>(name: ALIVE_DBCONTEXT)
    .AddCheck<CosmosHealthChecker>(name: ALIVE_COSMOS);

...

app.MapHealthChecks(
    "/cosmos",
    new HealthCheckOptions { Predicate = (c) => c.Name == ALIVE_COSMOS });
Вход в полноэкранный режим Выход из полноэкранного режима
public class CosmosHealthChecker : IHealthCheck
{
    private readonly IServiceScopeFactory _serviceScopeFactory;
    private readonly IConfiguration _configuration;
    private readonly ILogger<CosmosHealthChecker> _logger;

    public CosmosHealthChecker(
        IServiceScopeFactory scopeFactory,
        IConfiguration configuration,
        ILogger<CosmosHealthChecker> logger)
    {
        _serviceScopeFactory = scopeFactory;
        _configuration = configuration;
        _logger = logger;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default)
    {
        try
        {
            string collectionName = _configuration["Cosmos:Collection"];

            using IServiceScope scope = _serviceScopeFactory.CreateScope();
            Database db = scope.ServiceProvider.GetRequiredService<Database>();
            Container container = db.GetContainer(collectionName);

            await container.ReadContainerAsync(cancellationToken: cancellationToken);
            return new HealthCheckResult(HealthStatus.Healthy);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error pinging Cosmos db");

            return new HealthCheckResult(
                context.Registration.FailureStatus, "An unhealthy result.");
        }
    }
}
Войти в полноэкранный режим Выход из полноэкранного режима

Выводы

Эта статья объясняет, как мы можем обеспечить некоторые конечные точки проверки работоспособности в проекте Asp.Net всего несколькими строками кода 😎.

Как обычно, исходный код доступен в моем профиле на GitHub.


Фото Bruno Nascimento на Unsplash

Оцените статью
Procodings.ru
Добавить комментарий