ASP.NET Core 健康檢查
Health checks in ASP.NET Core, healthz,
引言
ASP.NET Core 提供健康情況檢查中介軟體和程式庫,用來報告應用程式基礎結構元件的健康情況。
健康狀態檢查是藉由實作 IHealthCheck 介面來建立。 CheckHealthAsync 方法會傳回指出狀態為 Healthy
、Degraded(性能下降)
或 Unhealthy
。
參考文件
實作方案
跳過實作過程,就實作成果來就是多一個專用的 url path: "/healthz",專用來顯示系統現況態是否健康。
也就是說實作方式不用遵行標準答案,只要有 "/healthz" Page 專用來探測系統現在健康狀態,如:資料庫連線、記憶體、網路等等資源用量是否處在正常狀態,這樣也滿足『健康檢查』的定義。
不過本例還是用標準答案來健檢。
開發環境
執行平台: .NET6 IDE: Visual Stuido 2022 語言: C# 10+ 框架: Blazor Server/WASM App
安裝套件 (optional)
第三方開發。用於健檢 SqlServer 連線狀態。
可以在 NuGet 套件管理找到名為 AspNetCore.HealthChecks.*
開頭的健檢資源。
NuGet\Install-Package AspNetCore.HealthChecks.SqlServer
關鍵源碼紀錄
var builder = WebApplication.CreateBuilder(args);
[...略...]
//## for 健康狀態檢查
builder.Services.AddHealthChecks()
.AddSqlServer(new HealthChecks.SqlServer.SqlServerHealthCheckOptions
{
// 第三方開發。用於健檢 SqlServer 連線狀態。
ConnectionString = ConnectionStringProvider.Get("MainDB"),
CommandText = "SELECT 1;"
}, name: "MainDB")
.AddCheck<SimpleHealthCheck>(nameof(SimpleHealthCheck));
var app = builder.Build(); //------ ------ ------
[...略...]
//## for 健康狀態檢查
app.MapHealthChecks("/healthz", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
ResponseWriter = SimpleHealthCheck.WriteHealthCheckUIResponse
});
//## Endpoints
app.MapControllers(); // for enable WebApi
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
自訂健康檢查類別 SimpleHealthCheck 。
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Text.Json;
using System.Text;
namespace YourProject.Models;
internal class SimpleHealthCheck : IHealthCheck
{
Task<HealthCheckResult> IHealthCheck.CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken)
{
try
{
return Task.FromResult(HealthCheckResult.Healthy("我很好。"));
}
catch (Exception ex)
{
return Task.FromResult(HealthCheckResult.Unhealthy(description: ex.Message, exception: ex));
}
}
/// <summary>
/// 專用資源,用於 HealthCheck 輸出。
/// 參考:[ASP.NET Core 中的健康狀態檢查-自訂輸出](https://learn.microsoft.com/zh-tw/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-6.0#customize-output)
/// </summary>
internal static Task WriteHealthCheckUIResponse(HttpContext context, HealthReport healthReport)
{
context.Response.ContentType = "application/json; charset=utf-8";
var options = new JsonWriterOptions {
Indented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
using var memoryStream = new MemoryStream();
using (var jsonWriter = new Utf8JsonWriter(memoryStream, options))
{
jsonWriter.WriteStartObject();
jsonWriter.WriteString("status", healthReport.Status.ToString());
jsonWriter.WriteStartObject("results");
foreach (var healthReportEntry in healthReport.Entries)
{
jsonWriter.WriteStartObject(healthReportEntry.Key);
jsonWriter.WriteString("status",
healthReportEntry.Value.Status.ToString());
jsonWriter.WriteString("description",
healthReportEntry.Value.Description);
jsonWriter.WriteStartObject("data");
foreach (var item in healthReportEntry.Value.Data)
{
jsonWriter.WritePropertyName(item.Key);
JsonSerializer.Serialize(jsonWriter, item.Value,
item.Value?.GetType() ?? typeof(object));
}
jsonWriter.WriteEndObject();
jsonWriter.WriteEndObject();
}
jsonWriter.WriteEndObject();
jsonWriter.WriteEndObject();
}
return context.Response.WriteAsync(
Encoding.UTF8.GetString(memoryStream.ToArray()));
}
}
第二種實作方式 補充 on 24-01-30
前述 app.MapHealthChecks("/healthz",...
是 miniapi 的實作方式,這方法無法被 Swagger 詮釋成文件。另一種方法是實作正統的 WebAPI 。此方法才能被 Swagger 詮譯成文件。
其中 HealthCheckService
會執行在 Services.AddHealthChecks()
段落註冊的所有健檢項目。
標準的寫法大概如下:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Net;
namespace YourProject.Controllers;
/// <summary>
/// for 健康狀態檢查
/// </summary>
[Route("healthz")]
[ApiController]
[AllowAnonymous]
public class HealthCheckController(HealthCheckService healthCheckService)
: ControllerBase
{
[HttpGet]
public async Task<ActionResult> Get()
{
///註:HealthCheckService 會執行在 Services.AddHealthChecks() 段落註冊的所有健檢項目。
HealthReport report = await healthCheckService.CheckHealthAsync();
var result = new
{
status = report.Status.ToString(),
errors = report.Entries.Select(e => new
{
name = e.Key,
status = e.Value.Status.ToString(),
description = e.Value.Description,
data = e.Value.Data
}),
};
return report.Status == HealthStatus.Healthy
? this.Ok(result)
: this.StatusCode((int)HttpStatusCode.ServiceUnavailable, result);
}
}
沒圖沒真象

Swagger + Rin + Healthz

完整原始碼
(EOF)
Last updated