ASP.NET Core 运行状况检查:返回预先评估的结果

Wae*_*her 5 c# health-monitoring asp.net-core health-check

我正在评估使用Microsoft Health Check来改进我们内部负载平衡器的路由。到目前为止,我对这个特性和它周围的社区提供的功能感到非常满意。但是,我还没有找到一件事,想问问是否可以开箱即用:

健康检查似乎会在收到请求后立即检索自己的状态。但是因为我们的服务可能很难在特定时刻处理大量请求,所以对 SQL Server 等第三方组件的查询可能需要时间来响应。因此,我们希望定期(例如每隔几秒)预评估该健康检查,并在调用健康检查 api 时返回该状态。

原因是,我们希望我们的负载均衡器尽快获得健康状态。对于我们的用例来说,使用预先评估的结果似乎已经足够了。

现在的问题是:是否可以向 ASP.NET Core 健康检查添加一种“轮询”或“自动更新”机制?或者这是否意味着我必须实现我自己的健康检查返回值从后台服务定期预评估结果?

请注意,我想对每个请求使用预先评估的结果,而不是 HTTP 缓存,其中实时结果为下一个请求缓存。

Wae*_*her 9

Panagiotis 的回答非常出色,给我带来了一个优雅的解决方案,我很乐意为下一个遇到这个问题的开发人员留下......

为了在不实现后台服务或任何计时器的情况下实现定期更新,我注册了一个IHealthCheckPublisher. 这样,ASP.NET Core 将定期自动运行注册的健康检查并将其结果发布到相应的实现。

在我的测试中,健康报告默认每 30 秒发布一次。

// add a publisher to cache the latest health report
services.AddSingleton<IHealthCheckPublisher, HealthReportCachePublisher>();
Run Code Online (Sandbox Code Playgroud)

我注册了我的实现HealthReportCachePublisher,它只不过是获取已发布的健康报告并将其保存在静态属性中。

我不太喜欢静态属性,但对我来说,它似乎足以满足这个用例。

/// <summary>
/// This publisher takes a health report and keeps it as "Latest".
/// Other health checks or endpoints can reuse the latest health report to provide
/// health check APIs without having the checks executed on each request.
/// </summary>
public class HealthReportCachePublisher : IHealthCheckPublisher
{
    /// <summary>
    /// The latest health report which got published
    /// </summary>
    public static HealthReport Latest { get; set; }

    /// <summary>
    /// Publishes a provided report
    /// </summary>
    /// <param name="report">The result of executing a set of health checks</param>
    /// <param name="cancellationToken">A task which will complete when publishing is complete</param>
    /// <returns></returns>
    public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
    {
        Latest = report;
        return Task.CompletedTask;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在真正的魔法发生在这里

正如每个运行状况检查示例中所见,我将运行状况检查映射到路由/health并使用UIResponseWriter.WriteHealthCheckUIResponse返回漂亮的 json 响应。

但我绘制了另一条路线/health/latest。在那里,谓词_ => false根本阻止执行​​任何健康检查。但我没有返回零运行状况检查的空结果,而是通过访问 static 来返回之前发布的运行状况报告HealthReportCachePublisher.Latest

app.UseEndpoints(endpoints =>
{
    // live health data: executes health checks for each request
    endpoints.MapHealthChecks("/health", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions()
    {
        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
    });

    // latest health report: won't execute health checks but return the cached data from the HealthReportCachePublisher
    endpoints.MapHealthChecks("/health/latest", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions()
    {
        Predicate = _ => false, // do not execute any health checks, we just want to return the latest health report
        ResponseWriter = (context, _) => UIResponseWriter.WriteHealthCheckUIResponse(context, HealthReportCachePublisher.Latest)
    });
});
Run Code Online (Sandbox Code Playgroud)

这样,调用/health就会通过对每个请求执行所有运行状况检查来返回实时运行状况报告。如果有很多事情需要检查或需要发出网络请求,这可能需要一段时间。

致电/health/latest将始终返回最新的预评估健康报告。这是非常快的,如果您有一个负载均衡器等待运行状况报告来相应地路由传入请求,那么这可能会很有帮助。


补充一点:上面的解决方案使用路由映射来取消健康检查的执行并返回最新的健康报告。正如所建议的,我尝试首先构建进一步的运行状况检查,该检查应返回最新的缓存运行状况报告,但这有两个缺点:

  • 返回缓存报告本身的新运行状况检查也会出现在结果中(或者必须按名称或标签进行调整)。
  • 没有简单的方法可以将缓存的运行状况报告映射到HealthCheckResult. 如果您复制属性和状态代码,这可能会起作用。但生成的 json 基本上是包含内部健康报告的健康报告。那不是你想要的。


Pan*_*vos 5

简洁版本

这已经可用并且已经可以与常见的监控系统集成。您也许可以将运行状况检查直接绑定到您的监控基础设施中。

细节

运行状况检查中间件通过实现IHealthCheckPublisher.PublishAsync接口方法的任何注册类定期向目标发布指标来解决这一问题。

services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();
Run Code Online (Sandbox Code Playgroud)

可以通过 HealthCheckPublisherOptions 配置发布。默认时间段为 30 秒。这些选项可用于添加延迟、过滤要运行的检查等:

services.Configure<HealthCheckPublisherOptions>(options =>
{
    options.Delay = TimeSpan.FromSeconds(2);
    options.Predicate = (check) => check.Tags.Contains("ready");
});
Run Code Online (Sandbox Code Playgroud)

一种选择是通过发布者缓存结果(HealthReport 实例),并从另一个 HealthCheck 端点提供它们。

也许更好的选择是将它们推送到 Application Insights 等监控系统或 Prometheus 等时间序列数据库。AspNetCore.Diagnostics.HealthCheck包为 App Insights、Seq、Datadog 和 Prometheus 提供大量现成的检查和发布程序。

普罗米修斯本身使用轮询。它定期调用所有注册的源来检索指标。虽然这适用于服务,但不适用于 CLI 应用程序等。因此,应用程序可以将结果推送到 Prometheus 网关,该网关会缓存指标,直到 Prometheus 本身请求它们为止。

services.AddHealthChecks()
        .AddSqlServer(connectionString: Configuration["Data:ConnectionStrings:Sample"])
        .AddCheck<RandomHealthCheck>("random")
        .AddPrometheusGatewayPublisher();
Run Code Online (Sandbox Code Playgroud)

除了推送到 Prometheus Gateway 之外,Prometheus 发布者还提供一个端点来通过包直接检索实时指标AspNetcore.HealthChecks.Publisher.Prometheus。其他应用程序可以使用相同的端点来检索这些指标:

// default endpoint: /healthmetrics
app.UseHealthChecksPrometheusExporter();
Run Code Online (Sandbox Code Playgroud)