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

时间:2020-10-07 14:15:50

标签: c# asp.net-core health-monitoring health-check

我正在评估使用Microsoft Health Checks来改善内部负载均衡器的路由。到目前为止,我对该功能和the community around it提供的功能非常满意。但是,有一件事我还没有找到,想问一下是否有可能开箱即用:

运行状况检查似乎一经请求便恢复其自身状态。但是由于在给定的时刻我们的服务可能很难处理大量请求,因此对诸如SQL Server之类的第三方组件的查询可能需要一些时间来响应。因此,我们希望定期(例如每隔几秒钟)预先评估该运行状况检查,并在调用运行状况检查api时返回该状态。

原因是,我们希望我们的负载均衡器尽快获得运行状况。对于我们的用例而言,使用预先评估的结果似乎已经足够了。

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

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

2 个答案:

答案 0 :(得分:3)

简短版本

这已经可用,并且可以与常见的监视系统集成。您也许可以将Health Check直接绑定到您的监视基础结构中。

详细信息

运行状况检查中间件通过实现publishing接口方法的任何注册类,通过定期向目标IHealthCheckPublisher.PublishAsync度量指标来覆盖此问题。

services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();

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

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

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

也许更好的选项是将其推送到诸如Application Insights之类的监视系统或诸如Prometheus之类的时间序列数据库中。 AspNetCore.Diagnostics.HealthCheck软件包为App Insights,Seq,Datadog和Prometheus提供了大量现成的支票和发布者。

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

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

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

// default endpoint: /healthmetrics
app.UseHealthChecksPrometheusExporter();

答案 1 :(得分:1)

Panagiotis的回答非常出色,这给我带来了一个优雅的解决方案,我想留给下一个对此感到困惑的开发人员...

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

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

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

我注册了我的实现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;
    }
}

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

从每个Health Checks样本中可以看出,我将运行状况检查映射到路由/health并使用UIResponseWriter.WriteHealthCheckUIResponse返回了一个漂亮的json响应。

但是我映射了另一条路线/health/latest。那里,谓词_ => false完全阻止了任何运行状况检查。但是,我没有返回零健康检查的空结果,而是通过访问静态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)
    });
});

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

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


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

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