Health Checks — /health endpoint, DbContext, Redis check

~15 daq · .NET Backend · API Sifati va Dizayn

Muammodan boshlaylik

Tunda PostgreSQL serveri to'ldi va to'xtadi. API jarayoni esa ishlab turibdi — port ochiq, ping javob beradi. Load balancer "server tirik" deb trafikni yuborishda davom etadi; har bir so'rov 30 soniya kutib, 500 qaytaradi. Ertalab mijozlar shikoyatidan bilasiz.

"Jarayon ishlayapti" va "ilova ishlashga tayyor" — turli narsalar.


Hayotiy o'xshatma

Smenaga kelgan haydovchi. U ishxonaga kelgan (jarayon tirik) — lekin mashinaga o'tirishdan oldin tekshiruvdan o'tadi: qon bosimi, hushyorlik (o'zi), mashinada yoqilg'i va tormoz (bog'liqliklar). Hammasi joyida bo'lsa — reysga chiqadi. Biror narsa noto'g'ri bo'lsa — dispetcher uni yo'lga chiqarmaydi va boshqa haydovchini yuboradi.


Asosiy sozlash

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/health");
GET /health
200 OK
Healthy

Holatlar:

Holat HTTP Ma'nosi
Healthy 200 Hammasi joyida
Degraded 200 Ishlayapti, lekin muammo bor (sekin, ixtiyoriy servis yo'q)
Unhealthy 503 Ishlay olmaydi

Bog'liqliklarni tekshirish

Baza

dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
builder.Services.AddHealthChecks()
    .AddDbContextCheck<DokonDbContext>("baza", tags: ["ready"]);

DbContext orqali bazaga ulanish (CanConnectAsync) tekshiriladi.

Tayyor paketlar

dotnet add package AspNetCore.HealthChecks.NpgSql
dotnet add package AspNetCore.HealthChecks.Redis
builder.Services.AddHealthChecks()
    .AddNpgSql(builder.Configuration.GetConnectionString("Dokon")!, name: "postgres", tags: ["ready"])
    .AddRedis(builder.Configuration.GetConnectionString("Redis")!, name: "redis",
        failureStatus: HealthStatus.Degraded, tags: ["ready"]);

Redis — cache (68-dars); u yiqilsa ilova sekinlashadi, lekin ishlaydi → Degraded, Unhealthy emas.

O'z tekshiruvingiz

public class TolovProvayderCheck(IHttpClientFactory f) : IHealthCheck
{
    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext ctx, CancellationToken ct = default)
    {
        try
        {
            var http = f.CreateClient("tolov");
            using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
            cts.CancelAfter(TimeSpan.FromSeconds(3));

            var javob = await http.GetAsync("/status", cts.Token);
            return javob.IsSuccessStatusCode
                ? HealthCheckResult.Healthy()
                : HealthCheckResult.Degraded(
quot;Provayder {(int)javob.StatusCode} qaytardi"); } catch (Exception ex) { return HealthCheckResult.Degraded("Provayder javob bermadi", ex); } } } builder.Services.AddHealthChecks().AddCheck<TolovProvayderCheck>("tolov", tags: ["ready"]);

Liveness va Readiness

Kubernetes va boshqa orkestratorlar ikki xil savol beradi:

Liveness — "tirikmisan?" Readiness — "trafik qabul qila olasanmi?"
Tekshiradi Jarayon osilib qolmaganmi Baza, Redis, bog'liqliklar
Muvaffaqiyatsiz bo'lsa Konteyner qayta ishga tushiriladi Trafik yuborilmaydi, qayta ishga tushirilmaydi
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = _ => false              // hech qanday tekshiruv — jarayon javob bersa, tirik
});

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = c => c.Tags.Contains("ready")
});

[!WARNING] Liveness ga baza tekshiruvini qo'ymang. Baza 2 daqiqa ishlamasa — barcha API konteynerlari "o'lik" deb hisoblanib, ketma-ket qayta ishga tushiriladi; baza qaytganda esa hammasi bir vaqtda ishga tushib, uni yana bo'g'adi. Bog'liqliklar — faqat readiness da.


Batafsil JSON javob

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = c => c.Tags.Contains("ready"),
    ResponseWriter = async (ctx, report) =>
    {
        ctx.Response.ContentType = "application/json";
        await ctx.Response.WriteAsJsonAsync(new
        {
            holat = report.Status.ToString(),
            davomiylikMs = report.TotalDuration.TotalMilliseconds,
            tekshiruvlar = report.Entries.Select(e => new
            {
                nomi = e.Key,
                holat = e.Value.Status.ToString(),
                tavsif = e.Value.Description,
                davomiylikMs = e.Value.Duration.TotalMilliseconds
            })
        });
    }
});
{
  "holat": "Degraded",
  "davomiylikMs": 42.7,
  "tekshiruvlar": [
    { "nomi": "postgres", "holat": "Healthy", "tavsif": null, "davomiylikMs": 12.1 },
    { "nomi": "redis", "holat": "Unhealthy", "tavsif": "Connection refused", "davomiylikMs": 30.2 }
  ]
}

Ochiq health endpoint da batafsil ma'lumot (versiyalar, xato matnlari) bermang — faqat ichki tarmoqqa yoki autentifikatsiya bilan:

app.MapHealthChecks("/health/batafsil", ...).RequireAuthorization("Admin");

Qayerda ishlatiladi

  • Docker — HEALTHCHECK (80-dars)
  • Kubernetes — livenessProbe, readinessProbe
  • Load balancer — sog'lom bo'lmagan serverga trafik yubormaslik
  • Monitoring (UptimeRobot, Grafana) — ogohlantirishlar

Health endpoint lari rate limit va autentifikatsiyadan ozod qilinadi ([AllowAnonymous], DisableRateLimiting) — aks holda orkestrator ularni tekshira olmaydi.


Tez-tez uchraydigan xatolar

Faqat /health → 200 — bog'liqliklar tekshirilmaydi, foydasi kam.

Og'ir tekshiruvlar (to'liq so'rov, katta jadval) — har 10 soniyada bazani yuklaydi; yengil SELECT 1.

Timeout siz tashqi tekshiruv — health endpoint o'zi osilib qoladi.


Xulosa

Health check — ilova va bog'liqliklar ishlashga tayyorligini bildiruvchi endpoint: AddHealthChecks + MapHealthChecks; Healthy (200), Degraded (200), Unhealthy (503). Bog'liqliklar: AddDbContextCheck, AddNpgSql, AddRedis; o'zingizniki — IHealthCheck.

Liveness (jarayon tirikmi — bog'liqliksiz) va readiness (trafikka tayyormi — teglar bilan) — alohida endpoint lar. Ixtiyoriy bog'liqlik — Degraded. Batafsil javob — faqat himoyalangan; tekshiruvlar yengil va timeout bilan.


Keyingi dars

75-dars: Minimal API — MapGet, endpoint filter, MapGroup