ASP.NET Core 中的运行状况检查

作者:Glenn CondronJuergen Gutsch

ASP.NET Core 提供运行状况检查中间件和库,以用于报告应用基础结构组件的运行状况。

运行状况检查由应用程序作为 HTTP 终结点公开。 可以为各种实时监视方案配置运行状况检查终结点:

  • 运行状况探测可以由容器业务流程协调程和负载均衡器用于检查应用的状态。 例如,容器业务流程协调程序可以通过停止滚动部署或重新启动容器来响应失败的运行状况检查。 负载均衡器可以通过将流量从失败的实例路由到正常实例,来应对不正常的应用。
  • 可以监视内存、磁盘和其他物理服务器资源的使用情况来了解是否处于正常状态。
  • 运行状况检查可以测试应用的依赖项(如数据库和外部服务终结点)以确认是否可用和正常工作。

运行状况检查通常与外部监视服务或容器业务流程协调程序一起用于检查应用的状态。 向应用添加运行状况检查之前,需确定要使用的监视系统。 监视系统决定了要创建的运行状况检查类型以及配置其终结点的方式。

基本运行状况探测

对于许多应用,报告应用在处理请求方面的可用性(运行情况)的基本运行状况探测配置足以发现应用的状态。

基本配置会注册运行状况检查服务,并调用运行状况检查中间件以通过运行状况响应在 URL 终结点处进行响应。 默认情况下,不会注册任何特定运行状况检查来测试任何特定依赖项或子系统。 如果能够在运行状况终结点 URL 处进行响应,则应用被视为正常。 默认响应编写器将 HealthStatus 作为纯文本响应写入客户端。 HealthStatusHealthStatus.HealthyHealthStatus.DegradedHealthStatus.Unhealthy

Program.cs 中使用 AddHealthChecks 注册运行状况检查服务。 通过调用 MapHealthChecks 来创建运行状况检查终结点。

以下示例在 /healthz 中创建运行状况检查终结点:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHealthChecks();

var app = builder.Build();

app.MapHealthChecks("/healthz");

app.Run();

Docker HEALTHCHECK

Docker 提供内置 HEALTHCHECK 指令,该指令可以用于检查使用基本运行状况检查配置的应用的状态:

HEALTHCHECK CMD curl --fail http://localhost:5000/healthz || exit

前面的示例使用 curl/healthz 的运行状况检查终结点发出 HTTP 请求。 curl 不包括在 .NET Linux 容器映像中,但可以通过在 Dockerfile 中安装所需的包来添加它。 使用基于 Alpine Linux 的映像的容器可以使用包含的 wget 代替 curl

创建运行状况检查

运行状况检查通过实现 IHealthCheck 接口进行创建。 CheckHealthAsync 方法会返回 HealthCheckResult,它以 HealthyDegradedUnhealthy 的形式指示运行状况。 结果被写成带有可配置状态代码的纯文本响应。 具体配置在运行状况检查选项部分中进行了介绍。 HealthCheckResult 还可以返回可选的键值对。

下面的示例演示运行状况检查的布局:

public class SampleHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        var isHealthy = true;

        // ...

        if (isHealthy)
        {
            return Task.FromResult(
                HealthCheckResult.Healthy("A healthy result."));
        }

        return Task.FromResult(
            new HealthCheckResult(
                context.Registration.FailureStatus, "An unhealthy result."));
    }
}

运行状况检查逻辑位于 CheckHealthAsync 方法中。 前面的示例将虚拟变量 isHealthy 设为 true。 如果 isHealthy 的值设为 false,则返回 HealthCheckRegistration.FailureStatus 状态。

如果 CheckHealthAsync 在检查过程中引发了异常,则会返回新的 HealthReportEntry,并将其 HealthReportEntry.Status 设置为 FailureStatus。 此状态由 AddCheck 定义(请参阅注册运行状况检查服务部分),其中包含导致检查失败的内部异常Description 设置为异常的消息。

注册运行状况检查服务

要注册运行状况检查服务,在 Program.cs 中调用 AddCheck

builder.Services.AddHealthChecks()
    .AddCheck<SampleHealthCheck>("Sample");

以下示例中显示的 AddCheck 重载会设置要在运行状况检查报告失败时报告的失败状态 (HealthStatus)。 如果失败状态设置为 null(默认值),则会报告 HealthStatus.Unhealthy。 此重载对于库创建者是一种十分有用的方案,在这种情况下,如果运行状况检查实现遵循该设置,则在发生运行状况检查失败时,应用会强制实施库所指示的失败状态。

标记可用于筛选运行状况检查。 筛选运行状况检查部分介绍了标记。

builder.Services.AddHealthChecks()
    .AddCheck<SampleHealthCheck>(
        "Sample",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "sample" });

AddCheck 还可以执行 lambda 函数。 在下面的示例中,运行状况检查始终返回正常结果:

builder.Services.AddHealthChecks()
    .AddCheck("Sample", () => HealthCheckResult.Healthy("A healthy result."));

调用 AddTypeActivatedCheck 将参数传递到运行状况检查实现。 在下面的示例中,一个类型激活的运行状况检查在其构造函数中接受一个整数和一个字符串:

public class SampleHealthCheckWithArgs : IHealthCheck
{
    private readonly int _arg1;
    private readonly string _arg2;

    public SampleHealthCheckWithArgs(int arg1, string arg2)
        => (_arg1, _arg2) = (arg1, arg2);

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        // ...

        return Task.FromResult(HealthCheckResult.Healthy("A healthy result."));
    }
}

若要注册上述运行状况检查,请调用 AddTypeActivatedCheck 并将该整数和字符串作为参数传递:

builder.Services.AddHealthChecks()
    .AddTypeActivatedCheck<SampleHealthCheckWithArgs>(
        "Sample",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "sample" },
        args: new object[] { 1, "Arg" });

使用运行状况检查路由

Program.cs 内,使用终结点 URL 或相对路径在终结点生成器上调用 MapHealthChecks

app.MapHealthChecks("/healthz");

需要主机

调用 RequireHost 以便为运行状况检查终结点指定一个或多个允许的主机。 主机应为 Unicode 而不是 punycode,且可以包含端口。 如果未提供集合,则接受任何主机:

app.MapHealthChecks("/healthz")
    .RequireHost("www.contoso.com:5001");

若要将运行状况检查终结点限制为仅响应特定端口,请在对 RequireHost 的调用中指定端口。 此方法通常用于在容器环境中公开用于监视服务的端口:

app.MapHealthChecks("/healthz")
    .RequireHost("*:5001");

有关详细信息,请参阅路由中与 RequireHost 匹配的主机

需要授权

调用 RequireAuthorization 以在状况检查请求终结点上运行身份验证中间件。 RequireAuthorization 重载接受一个或多个授权策略。 如果未提供策略,则使用默认的授权策略:

app.MapHealthChecks("/healthz")
    .RequireAuthorization();

启用跨域请求 (CORS)

尽管从浏览器手动运行运行状况检查不是常见的使用方案,但可以通过在运行状况检查终结点上调用 RequireCors 来启用 CORS 中间件。 RequireCors 重载接受 CORS 策略生成器委托 (CorsPolicyBuilder) 或策略名称。 有关详细信息,请参阅在 ASP.NET Core 中启用跨源请求 (CORS)

运行状况检查选项

HealthCheckOptions 使你可以自定义运行状况检查行为:

筛选运行状况检查

默认情况下,运行状况检查中间件会运行所有已注册的运行状况检查。 若要运行运行状况检查的子集,请提供向 Predicate 选项返回布尔值的函数。

下面的示例筛选运行状况检查,以便仅运行带有 sample 标记的运行状况检查:

app.MapHealthChecks("/healthz", new HealthCheckOptions
{
    Predicate = healthCheck => healthCheck.Tags.Contains("sample")
});

自定义 HTTP 状态代码

使用 ResultStatusCodes 可自定义运行状况状态到 HTTP 状态代码的映射。 以下 StatusCodes 分配是中间件所使用的默认值。 更改状态代码值以满足要求:

app.MapHealthChecks("/healthz", new HealthCheckOptions
{
    ResultStatusCodes =
    {
        [HealthStatus.Healthy] = StatusCodes.Status200OK,
        [HealthStatus.Degraded] = StatusCodes.Status200OK,
        [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
    }
});

取消缓存标头

AllowCachingResponses 控制运行状况检查中间件是否将 HTTP 标头添加到探测响应以防止响应缓存。 如果值为 false(默认值),则中间件会设置或替代 Cache-ControlExpiresPragma 标头以防止响应缓存。 如果值为 true,则中间件不会修改响应的缓存标头:

app.MapHealthChecks("/healthz", new HealthCheckOptions
{
    AllowCachingResponses = false
});

自定义输出

若要自定义运行状况检查报告的输出,请将 HealthCheckOptions.ResponseWriter 属性设置为写入响应的委托:

app.MapHealthChecks("/healthz", new HealthCheckOptions
{
    ResponseWriter = WriteResponse
});

默认委托会使用 HealthReport.Status 字符串值编写最小的纯文本响应。 以下自定义委托使用 System.Text.Json 输出自定义 JSON 响应:

private static Task WriteResponse(HttpContext context, HealthReport healthReport)
{
    context.Response.ContentType = "application/json; charset=utf-8";

    var options = new JsonWriterOptions { Indented = true };

    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()));
}

运行状况检查 API 不为复杂 JSON 返回格式提供内置支持,因为该格式特定于你选择的监视系统。 必要时自定义上述示例中的响应。 有关使用 System.Text.Json 执行 JSON 序列化的详细信息,请参阅如何在 .NET 中序列化和反序列化 JSON

数据库探测

运行状况检查可以指定数据库查询作为布尔测试来运行,以指示数据库是否在正常响应。

AspNetCore.Diagnostics.HealthChecks 是一个适用于 ASP.NET Core 应用的运行状况检查库,包括针对 SQL Server 数据库运行的运行状况检查。 AspNetCore.Diagnostics.HealthChecks 对数据库执行 SELECT 1 查询以确认与数据库的连接是否正常。

警告

使用查询检查数据库连接时,请选择快速返回的查询。 查询方法会面临使数据库过载和降低其性能的风险。 在大多数情况下,无需运行测试查询。 只需建立成功的数据库连接便足矣。 如果发现需要运行查询,请选择简单的 SELECT 查询,如 SELECT 1

若要使用此 SQL Server 运行状况检查,请包含对 AspNetCore.HealthChecks.SqlServer NuGet 包的包引用。 以下示例注册了 SQL Server 运行状况检查:

builder.Services.AddHealthChecks()
    .AddSqlServer(
        builder.Configuration.GetConnectionString("DefaultConnection"));

注意

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

Entity Framework Core DbContext 探测

DbContext 检查确认应用可以与为 EF CoreDbContext 配置的数据库通信。 满足以下条件的应用支持 DbContext 检查:

AddDbContextCheckDbContext 注册运行状况检查。 DbContext 作为 TContext 提供给方法。 重载可用于配置失败状态、标记和自定义测试查询。

默认情况下:

  • DbContextHealthCheck 调用 EF Core 的 CanConnectAsync 方法。 可以自定义在使用 AddDbContextCheck 方法重载检查运行状况时运行的操作。
  • 运行状况检查的名称是 TContext 类型的名称。

以下示例注册了一个 DbContext 和一个关联的 DbContextHealthCheck

builder.Services.AddDbContext<SampleDbContext>(options =>
    options.UseSqlServer(
        builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddHealthChecks()
    .AddDbContextCheck<SampleDbContext>();

单独的就绪情况和运行情况探测

在某些托管方案中,使用一对运行状况检查来区分两种应用状态:

  • “就绪情况”指示情况是否为应用正常运行,但未准备好接收请求。
  • “运行情况”指示情况是否为应用已崩溃且必须重启。

请看下面的示例:应用必须下载大型配置文件才能处理请求。 如果初始下载失败,我们不希望重启该应用,因为该应用可以多次尝试下载该文件。 我们使用运行情况探测来描述进程的运行情况,不运行其他检查。 我们还想要在配置文件下载成功之前阻止将请求发送到应用。 我们使用就绪情况探测指示“未就绪”状态,直到下载成功并且应用已准备好接收请求。

以下后台任务模拟了一个启动过程,大约需要 15 秒。 完成后,任务将 StartupHealthCheck.StartupCompleted 属性设置为 true:

public class StartupBackgroundService : BackgroundService
{
    private readonly StartupHealthCheck _healthCheck;

    public StartupBackgroundService(StartupHealthCheck healthCheck)
        => _healthCheck = healthCheck;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // Simulate the effect of a long-running task.
        await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);

        _healthCheck.StartupCompleted = true;
    }
}

StartupHealthCheck 报告长时间运行的启动任务的完成,并公开由后台服务设置的 StartupCompleted 属性:

public class StartupHealthCheck : IHealthCheck
{
    private volatile bool _isReady;

    public bool StartupCompleted
    {
        get => _isReady;
        set => _isReady = value;
    }

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        if (StartupCompleted)
        {
            return Task.FromResult(HealthCheckResult.Healthy("The startup task has completed."));
        }

        return Task.FromResult(HealthCheckResult.Unhealthy("That startup task is still running."));
    }
}

运行状况检查在 Program.cs 中使用 AddCheck 与托管服务一起注册。 因为托管服务必须对运行状况检查设置该属性,所以运行状况检查也会在服务容器中注册为单一实例:

builder.Services.AddHostedService<StartupBackgroundService>();
builder.Services.AddSingleton<StartupHealthCheck>();

builder.Services.AddHealthChecks()
    .AddCheck<StartupHealthCheck>(
        "Startup",
        tags: new[] { "ready" });

若要创建两个不同的运行状况检查终结点,请调用 MapHealthChecks 两次:

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

app.MapHealthChecks("/healthz/live", new HealthCheckOptions
{
    Predicate = _ => false
});

前面的示例创建了以下运行状况检查终结点:

  • /healthz/ready(用于就绪状态检查)。 就绪检查将运行状况检查筛选为带有 ready 标记的运行状况检查。
  • /healthz/live(用于运行情况检查)。 运行情况检查通过在 HealthCheckOptions.Predicate 委托中返回 false 来筛选出所有运行状况检查。 有关筛选运行状况检查的详细信息,请参阅本文中的筛选运行状况检查

启动任务完成之前,/healthz/ready 终结点将报告 Unhealthy 状态。 启动任务完成之后,此终结点将报告 Healthy 状态。 /healthz/live 终结点将排除所有检查并报告所有调用的 Healthy 状态。

Kubernetes 示例

在诸如 Kubernetes 这类环境中,使用单独的就绪情况和运行情况检查会十分有用。 在 Kubernetes 中,应用可能需要在接受请求之前运行耗时的启动工作,如基础数据库可用性测试。 使用单独检查使业务流程协调程序可以区分应用是否正常运行但尚未准备就绪,或是应用程序是否未能启动。 有关 Kubernetes 中的就绪情况和运行情况探测的详细信息,请参阅 Kubernetes 文档中的配置运行情况和就绪情况探测

以下示例演示如何使用 Kubernetes 就绪情况探测配置:

spec:
  template:
  spec:
    readinessProbe:
      # an http probe
      httpGet:
        path: /healthz/ready
        port: 80
      # length of time to wait for a pod to initialize
      # after pod startup, before applying health checking
      initialDelaySeconds: 30
      timeoutSeconds: 1
    ports:
      - containerPort: 80

分发运行状况检查库

将运行状况检查作为库进行分发:

  1. 编写将 IHealthCheck 接口作为独立类来实现的运行状况检查。 该类可以依赖于依赖关系注入 (DI)类型激活和命名选项来访问配置数据。

  2. 使用参数编写一个扩展方法,所使用的应用会在其 Program.cs 方法中调用它。 来看看以下示例运行状况检查,它接受 arg1arg2 作为构造函数参数:

    public SampleHealthCheckWithArgs(int arg1, string arg2)
        => (_arg1, _arg2) = (arg1, arg2);
    

    前面的签名指示运行状况检查需要自定义数据来处理运行状况检查探测逻辑。 当运行状况检查向扩展方法注册时,数据会提供给用于创建运行状况检查实例的委托。 在以下示例中,调用方会指定:

    • arg1:运行状况检查的整数数据点。
    • arg2:运行状况检查的字符串参数。
    • name:可选的运行状况检查名称。 如果为 null,则使用默认值。
    • failureStatus:一个可选的 HealthStatus,对失败状态的报告。 如果为 null,则使用 HealthStatus.Unhealthy
    • tags:可选的 IEnumerable<string> 标记集合。
    public static class SampleHealthCheckBuilderExtensions
    {
        private const string DefaultName = "Sample";
    
        public static IHealthChecksBuilder AddSampleHealthCheck(
            this IHealthChecksBuilder healthChecksBuilder,
            int arg1,
            string arg2,
            string? name = null,
            HealthStatus? failureStatus = null,
            IEnumerable<string>? tags = default)
        {
            return healthChecksBuilder.Add(
                new HealthCheckRegistration(
                    name ?? DefaultName,
                    _ => new SampleHealthCheckWithArgs(arg1, arg2),
                    failureStatus,
                    tags));
        }
    }
    

运行状况检查发布服务器

IHealthCheckPublisher 添加到服务容器时,运行状况检查系统,会定期执行运行状况检查并使用结果调用 PublishAsync。 在期望每个进程定期调用监视系统以便确定运行状况的基于推送的运行状况监视系统方案中,此过程十分有用。

使用 HealthCheckPublisherOptions 可设置:

  • Delay:在应用启动后且在应用执行 IHealthCheckPublisher 实例之前所应用的初始延迟。 延迟在启动时应用一次,不适用于后续迭代。 默认值为 5 秒。
  • PeriodIHealthCheckPublisher 执行的时间。 默认值为 30 秒。
  • Predicate:如果 Predicatenull(默认值),则运行状况检查发布服务器服务运行所有已注册的运行状况检查。 若要运行运行状况检查的子集,请提供用于筛选检查集的函数。 每个时间段都会评估谓词。
  • Timeout:执行所有 IHealthCheckPublisher 实例的运行状况检查的超时时间。 在不超时的情况下,使用 InfiniteTimeSpan 执行。 默认值为 30 秒。

下面的示例演示运行状况发布服务器的布局:

public class SampleHealthCheckPublisher : IHealthCheckPublisher
{
    public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
    {
        if (report.Status == HealthStatus.Healthy)
        {
            // ...
        }
        else
        {
            // ...
        }

        return Task.CompletedTask;
    }
}

HealthCheckPublisherOptions 类提供了用于配置运行状况检查发布服务器的行为的属性。

以下示例将运行状况检查发布服务器注册为单一实例并配置 HealthCheckPublisherOptions

builder.Services.Configure<HealthCheckPublisherOptions>(options =>
{
    options.Delay = TimeSpan.FromSeconds(2);
    options.Predicate = healthCheck => healthCheck.Tags.Contains("sample");
});

builder.Services.AddSingleton<IHealthCheckPublisher, SampleHealthCheckPublisher>();

注意

AspNetCore.Diagnostics.HealthChecks 包括多个系统的发布服务器(包括 Application Insights)。

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

依赖关系注入和运行状况检查

可以使用依赖关系注入来使用运行状况检查类中特定 Type 的实例。 依赖关系注入可用于将选项或全局配置注入运行状况检查。 使用依赖关系注入不是配置运行状况检查的常见方案。 通常,每个运行状况检查对于实际测试都相当具体,并使用 IHealthChecksBuilder 扩展方法进行配置。

以下示例演示了一个示例运行状况检查,用于通过依赖关系注入检索配置对象:

public class SampleHealthCheckWithDI : IHealthCheck
{
    private readonly SampleHealthCheckWithDiConfig _config;

    public SampleHealthCheckWithDI(SampleHealthCheckWithDiConfig config)
        => _config = config;

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        var isHealthy = true;

        // use _config ...

        if (isHealthy)
        {
            return Task.FromResult(
                HealthCheckResult.Healthy("A healthy result."));
        }

        return Task.FromResult(
            new HealthCheckResult(
                context.Registration.FailureStatus, "An unhealthy result."));
    }
}

SampleHealthCheckWithDiConfig 和运行状况检查需要添加到服务容器中:

builder.Services.AddSingleton<SampleHealthCheckWithDiConfig>(new SampleHealthCheckWithDiConfig
{
    BaseUriToCheck = new Uri("https://sample.contoso.com/api/")
});
builder.Services.AddHealthChecks()
    .AddCheck<SampleHealthCheckWithDI>(
        "With Dependency Injection",
        tags: new[] { "inject" });

其他资源

ASP.NET Core 提供运行状况检查中间件和库,以用于报告应用基础结构组件的运行状况。

运行状况检查由应用程序作为 HTTP 终结点公开。 可以为各种实时监视方案配置运行状况检查终结点:

  • 运行状况探测可以由容器业务流程协调程和负载均衡器用于检查应用的状态。 例如,容器业务流程协调程序可以通过停止滚动部署或重新启动容器来响应失败的运行状况检查。 负载均衡器可以通过将流量从失败的实例路由到正常实例,来应对不正常的应用。
  • 可以监视内存、磁盘和其他物理服务器资源的使用情况来了解是否处于正常状态。
  • 运行状况检查可以测试应用的依赖项(如数据库和外部服务终结点)以确认是否可用和正常工作。

查看或下载示例代码如何下载

示例应用包含本文中所述的方案示例。 若要运行给定方案的示例应用,请在命令行界面中从项目文件夹中使用 dotnet run 命令。 请参阅示例应用的 README.md 文件和本文中的方案说明,详细了解如何使用示例应用。

先决条件

运行状况检查通常与外部监视服务或容器业务流程协调程序一起用于检查应用的状态。 向应用添加运行状况检查之前,需确定要使用的监视系统。 监视系统决定了要创建的运行状况检查类型以及配置其终结点的方式。

ASP.NET Core 应用隐式引用 Microsoft.AspNetCore.Diagnostics.HealthChecks 包。 若要使用 Entity Framework Core 运行运行状况检查,请添加对 Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 包的引用。

示例应用提供了启动代码来演示几个方案的运行状况检查。 数据库探测方案使用 AspNetCore.Diagnostics.HealthChecks 检查数据库连接的运行状况。 DbContext 探测方案使用 EF CoreDbContext 检查数据库。 若要探索数据库方案,示例应用将:

注意

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

另一个运行状况检查方案演示如何将运行状况检查筛选到某个管理端口。 示例应用要求创建包含管理 URL 和管理端口的 Properties/launchSettings.json 文件。 有关详细信息,请参阅按端口筛选部分。

基本运行状况探测

对于许多应用,报告应用在处理请求方面的可用性(运行情况)的基本运行状况探测配置足以发现应用的状态。

基本配置会注册运行状况检查服务,并调用运行状况检查中间件以通过运行状况响应在 URL 终结点处进行响应。 默认情况下,不会注册任何特定运行状况检查来测试任何特定依赖项或子系统。 如果能够在运行状况终结点 URL 处进行响应,则应用被视为正常。 默认响应编写器会以纯文本响应形式将状态 (HealthStatus) 写回到客户端,以便指示 HealthStatus.HealthyHealthStatus.DegradedHealthStatus.Unhealthy 状态。

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点。

在示例应用中,在 /health 处创建运行状况检查终结点 (BasicStartup.cs):

public class BasicStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHealthChecks();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHealthChecks("/health");
        });
    }
}

若要使用示例应用运行基本配置方案,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario basic

Docker 示例

Docker 提供内置 HEALTHCHECK 指令,该指令可以用于检查使用基本运行状况检查配置的应用的状态:

HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit

创建运行状况检查

运行状况检查通过实现 IHealthCheck 接口进行创建。 CheckHealthAsync 方法会返回 HealthCheckResult,它以 HealthyDegradedUnhealthy 的形式指示运行状况。 结果会使用可配置状态代码(配置在运行状况检查选项部分中进行介绍)编写为纯文本响应。 HealthCheckResult 还可以返回可选的键值对。

下面的 ExampleHealthCheck 类演示运行状况检查的布局。 运行状况检查逻辑位于 CheckHealthAsync 方法中。 以下示例将虚拟变量 healthCheckResultHealthy 设为 true。 如果 healthCheckResultHealthy 的值设为 false,则返回 HealthCheckRegistration.FailureStatus 状态。

public class ExampleHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var healthCheckResultHealthy = true;

        if (healthCheckResultHealthy)
        {
            return Task.FromResult(
                HealthCheckResult.Healthy("A healthy result."));
        }

        return Task.FromResult(
            new HealthCheckResult(context.Registration.FailureStatus, 
            "An unhealthy result."));
    }
}

如果 CheckHealthAsync 在检查过程中引发了异常,则会返回新的 HealthReportEntry,并将其 HealthReportEntry.Status 设置为 FailureStatus,这由 AddCheck 定义(请参阅注册运行状况检查服务部分),其中包含最初导致检查失败的内部异常Description 设置为异常的消息。

注册运行状况检查服务

ExampleHealthCheck 类型使用 Startup.ConfigureServices 中的 AddCheck 添加到运行状况检查服务:

services.AddHealthChecks()
    .AddCheck<ExampleHealthCheck>("example_health_check");

以下示例中显示的 AddCheck 重载会设置要在运行状况检查报告失败时报告的失败状态 (HealthStatus)。 如果失败状态设置为 null(默认值),则会报告 HealthStatus.Unhealthy。 此重载对于库创建者是一种十分有用的方案,在这种情况下,如果运行状况检查实现遵循该设置,则在发生运行状况检查失败时,应用会强制实施库所指示的失败状态。

标记用于筛选运行状况检查(在筛选运行状况检查部分中进行了进一步介绍)。

services.AddHealthChecks()
    .AddCheck<ExampleHealthCheck>(
        "example_health_check",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "example" });

AddCheck 还可以执行 lambda 函数。 在以下示例中,运行状况检查名称指定为 Example,并且检查始终返回正常状态:

services.AddHealthChecks()
    .AddCheck("Example", () =>
        HealthCheckResult.Healthy("Example is OK!"), tags: new[] { "example" });

调用 AddTypeActivatedCheck 将参数传递到运行状况检查实现。 在以下示例中,TestHealthCheckWithArgs 接受一个整数和一个字符串,以便在调用 CheckHealthAsync 时使用:

private class TestHealthCheckWithArgs : IHealthCheck
{
    public TestHealthCheckWithArgs(int i, string s)
    {
        I = i;
        S = s;
    }

    public int I { get; set; }

    public string S { get; set; }

    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, 
        CancellationToken cancellationToken = default)
    {
        ...
    }
}

TestHealthCheckWithArgs 通过使用传递到实现的整数和字符串调用 AddTypeActivatedCheck 来注册:

services.AddHealthChecks()
    .AddTypeActivatedCheck<TestHealthCheckWithArgs>(
        "test", 
        failureStatus: HealthStatus.Degraded, 
        tags: new[] { "example" }, 
        args: new object[] { 5, "string" });

使用运行状况检查路由

Startup.Configure 内,使用终结点 URL 或相对路径在终结点生成器上调用 MapHealthChecks

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health");
});

需要主机

调用 RequireHost 以便为运行状况检查终结点指定一个或多个允许的主机。 主机应为 Unicode 而不是 punycode,且可以包含端口。 如果未提供集合,则接受任何主机。

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health").RequireHost("www.contoso.com:5001");
});

有关详细信息,请参阅按端口筛选部分。

需要授权

调用 RequireAuthorization 以在状况检查请求终结点上运行身份验证中间件。 RequireAuthorization 重载接受一个或多个授权策略。 如果未提供策略,则使用默认的授权策略。

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health").RequireAuthorization();
});

启用跨域请求 (CORS)

尽管从浏览器手动运行运行状况检查不是常见的使用方案,但可以通过在运行状况检查终结点上调用 RequireCors 来启用 CORS 中间件。 RequireCors 重载接受 CORS 策略生成器委托 (CorsPolicyBuilder) 或策略名称。 如果未提供策略,则使用默认的 CORS 策略。 有关详细信息,请参阅在 ASP.NET Core 中启用跨源请求 (CORS)

运行状况检查选项

HealthCheckOptions 使你可以自定义运行状况检查行为:

筛选运行状况检查

默认情况下,运行状况检查中间件会运行所有已注册的运行状况检查。 若要运行运行状况检查的子集,请提供向 Predicate 选项返回布尔值的函数。 在以下示例中,Bar 运行状况检查在函数条件语句 中由于其标记 (bar_tag) 而被筛选掉,在条件语句中,仅当运行状况检查的 Tags 属性与 foo_tagbaz_tag 匹配时才返回 true

Startup.ConfigureServices中:

services.AddHealthChecks()
    .AddCheck("Foo", () =>
        HealthCheckResult.Healthy("Foo is OK!"), tags: new[] { "foo_tag" })
    .AddCheck("Bar", () =>
        HealthCheckResult.Unhealthy("Bar is unhealthy!"), tags: new[] { "bar_tag" })
    .AddCheck("Baz", () =>
        HealthCheckResult.Healthy("Baz is OK!"), tags: new[] { "baz_tag" });

Startup.Configure 中,Predicate 筛选出“Bar”运行状况检查。 仅 Foo 和 Baz 执行:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        Predicate = (check) => check.Tags.Contains("foo_tag") ||
            check.Tags.Contains("baz_tag")
    });
});

自定义 HTTP 状态代码

使用 ResultStatusCodes 可自定义运行状况状态到 HTTP 状态代码的映射。 以下 StatusCodes 分配是中间件所使用的默认值。 更改状态代码值以满足要求。

Startup.Configure中:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        ResultStatusCodes =
        {
            [HealthStatus.Healthy] = StatusCodes.Status200OK,
            [HealthStatus.Degraded] = StatusCodes.Status200OK,
            [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
        }
    });
});

取消缓存标头

AllowCachingResponses 控制运行状况检查中间件是否将 HTTP 标头添加到探测响应以防止响应缓存。 如果值为 false(默认值),则中间件会设置或替代 Cache-ControlExpiresPragma 标头以防止响应缓存。 如果值为 true,则中间件不会修改响应的缓存标头。

Startup.Configure中:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        AllowCachingResponses = false
    });
});

自定义输出

Startup.Configure 中,将 HealthCheckOptions.ResponseWriter 选项设置为编写响应的委托:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        ResponseWriter = WriteResponse
    });
});

默认委托会使用 HealthReport.Status 字符串值编写最小的纯文本响应。 以下自定义委托输出自定义 JSON 响应。

示例应用中的第一个示例演示如何使用 System.Text.Json

private static Task WriteResponse(HttpContext context, HealthReport result)
{
    context.Response.ContentType = "application/json; charset=utf-8";

    var options = new JsonWriterOptions
    {
        Indented = true
    };

    using (var stream = new MemoryStream())
    {
        using (var writer = new Utf8JsonWriter(stream, options))
        {
            writer.WriteStartObject();
            writer.WriteString("status", result.Status.ToString());
            writer.WriteStartObject("results");
            foreach (var entry in result.Entries)
            {
                writer.WriteStartObject(entry.Key);
                writer.WriteString("status", entry.Value.Status.ToString());
                writer.WriteString("description", entry.Value.Description);
                writer.WriteStartObject("data");
                foreach (var item in entry.Value.Data)
                {
                    writer.WritePropertyName(item.Key);
                    JsonSerializer.Serialize(
                        writer, item.Value, item.Value?.GetType() ??
                        typeof(object));
                }
                writer.WriteEndObject();
                writer.WriteEndObject();
            }
            writer.WriteEndObject();
            writer.WriteEndObject();
        }

        var json = Encoding.UTF8.GetString(stream.ToArray());

        return context.Response.WriteAsync(json);
    }
}

第二个示例演示如何使用 Newtonsoft.Json

private static Task WriteResponse(HttpContext context, HealthReport result)
{
    context.Response.ContentType = "application/json";

    var json = new JObject(
        new JProperty("status", result.Status.ToString()),
        new JProperty("results", new JObject(result.Entries.Select(pair =>
            new JProperty(pair.Key, new JObject(
                new JProperty("status", pair.Value.Status.ToString()),
                new JProperty("description", pair.Value.Description),
                new JProperty("data", new JObject(pair.Value.Data.Select(
                    p => new JProperty(p.Key, p.Value))))))))));

    return context.Response.WriteAsync(
        json.ToString(Formatting.Indented));
}

在示例应用中,注释掉 CustomWriterStartup.cs 中的 SYSTEM_TEXT_JSON预处理器指令,以启用 WriteResponseNewtonsoft.Json 版本。

运行状况检查 API 不为复杂 JSON 返回格式提供内置支持,因为该格式特定于你选择的监视系统。 必要时自定义上述示例中的响应。 有关使用 System.Text.Json 执行 JSON 序列化的详细信息,请参阅如何在 .NET 中序列化和反序列化 JSON

数据库探测

运行状况检查可以指定数据库查询作为布尔测试来运行,以指示数据库是否在正常响应。

示例应用使用 AspNetCore.Diagnostics.HealthChecks(适用于 ASP.NET Core 应用的运行状况检查库)对 SQL Server 数据库运行运行状况检查。 AspNetCore.Diagnostics.HealthChecks 对数据库执行 SELECT 1 查询以确认与数据库的连接是否正常。

警告

使用查询检查数据库连接时,请选择快速返回的查询。 查询方法会面临使数据库过载和降低其性能的风险。 在大多数情况下,无需运行测试查询。 只需建立成功的数据库连接便足矣。 如果发现需要运行查询,请选择简单的 SELECT 查询,如 SELECT 1

添加对 AspNetCore.HealthChecks.SqlServer 的包引用。

在示例应用的 appsettings.json 文件中提供有效数据库连接字符串。 应用使用名为 HealthCheckSample 的 SQL Server 数据库:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=HealthCheckSample;Trusted_Connection=True;MultipleActiveResultSets=true;ConnectRetryCount=0"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    },
    "Console": {
      "IncludeScopes": "true"
    }
  },
  "AllowedHosts": "*"
}

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 示例应用使用数据库的连接字符串 (DbHealthStartup.cs) 调用 AddSqlServer 方法:

services.AddHealthChecks()
    .AddSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);

通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health");
}

若要使用示例应用运行数据库探测方案,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario db

注意

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

Entity Framework Core DbContext 探测

DbContext 检查确认应用可以与为 EF CoreDbContext 配置的数据库通信。 满足以下条件的应用支持 DbContext 检查:

AddDbContextCheck<TContext>DbContext 注册运行状况检查。 DbContext 作为方法的 TContext 提供。 重载可用于配置失败状态、标记和自定义测试查询。

默认情况下:

  • DbContextHealthCheck 调用 EF Core 的 CanConnectAsync 方法。 可以自定义在使用 AddDbContextCheck 方法重载检查运行状况时运行的操作。
  • 运行状况检查的名称是 TContext 类型的名称。

在示例应用中,AppDbContext 会提供给 AddDbContextCheck,并在 Startup.ConfigureServices 中注册为服务 (DbContextHealthStartup.cs)。

services.AddHealthChecks()
    .AddDbContextCheck<AppDbContext>();

services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(
        Configuration["ConnectionStrings:DefaultConnection"]);
});

通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health");
}

若要使用示例应用运行 DbContext 探测方案,请确认连接字符串指定的数据库在 SQL Server 实例中不存在。 如果该数据库存在,请删除它。

在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario dbcontext

在应用运行之后,在浏览器中对 /health 终结点发出请求,从而检查运行状况。 数据库和 AppDbContext 不存在,因此应用提供以下响应:

Unhealthy

触发示例应用以创建数据库。 向 /createdatabase 发出请求。 应用会进行以下响应:

Creating the database...
Done!
Navigate to /health to see the health status.

/health 终结点发出请求。 数据库和上下文存在,因此应用会进行以下响应:

Healthy

触发示例应用以删除数据库。 向 /deletedatabase 发出请求。 应用会进行以下响应:

Deleting the database...
Done!
Navigate to /health to see the health status.

/health 终结点发出请求。 应用提供不正常的响应:

Unhealthy

单独的就绪情况和运行情况探测

在某些托管方案中,使用一对运行状况检查来区分两种应用状态:

  • “就绪情况”指示情况是否为应用正常运行,但未准备好接收请求。
  • “运行情况”指示情况是否为应用已崩溃且必须重启。

请看下面的示例:应用必须下载大型配置文件才能处理请求。 如果初始下载失败,我们不希望重启该应用,因为该应用可以多次尝试下载该文件。 我们使用运行情况探测来描述进程的运行情况,不运行其他检查。 我们还想要在配置文件下载成功之前阻止将请求发送到应用。 我们使用就绪情况探测指示“未就绪”状态,直到下载成功并且应用已准备好接收请求。

示例应用包含运行状况检查,以报告托管服务中长时间运行的启动任务的完成。 StartupHostedServiceHealthCheck 公开了属性 StartupTaskCompleted,托管服务在其长时间运行的任务完成时可以将该属性设置为 true (StartupHostedServiceHealthCheck.cs):

public class StartupHostedServiceHealthCheck : IHealthCheck
{
    private volatile bool _startupTaskCompleted = false;

    public string Name => "slow_dependency_check";

    public bool StartupTaskCompleted
    {
        get => _startupTaskCompleted;
        set => _startupTaskCompleted = value;
    }

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        if (StartupTaskCompleted)
        {
            return Task.FromResult(
                HealthCheckResult.Healthy("The startup task is finished."));
        }

        return Task.FromResult(
            HealthCheckResult.Unhealthy("The startup task is still running."));
    }
}

长时间运行的后台任务由托管服务 (Services/StartupHostedService) 启动。 在该任务结束时,StartupHostedServiceHealthCheck.StartupTaskCompleted 设置为 true

public class StartupHostedService : IHostedService, IDisposable
{
    private readonly int _delaySeconds = 15;
    private readonly ILogger _logger;
    private readonly StartupHostedServiceHealthCheck _startupHostedServiceHealthCheck;

    public StartupHostedService(ILogger<StartupHostedService> logger,
        StartupHostedServiceHealthCheck startupHostedServiceHealthCheck)
    {
        _logger = logger;
        _startupHostedServiceHealthCheck = startupHostedServiceHealthCheck;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Startup Background Service is starting.");

        // Simulate the effect of a long-running startup task.
        Task.Run(async () =>
        {
            await Task.Delay(_delaySeconds * 1000);

            _startupHostedServiceHealthCheck.StartupTaskCompleted = true;

            _logger.LogInformation("Startup Background Service has started.");
        });

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Startup Background Service is stopping.");

        return Task.CompletedTask;
    }

    public void Dispose()
    {
    }
}

运行状况检查在 Startup.ConfigureServices 中使用 AddCheck 与托管服务一起注册。 因为托管服务必须对运行状况检查设置该属性,所以运行状况检查也会在服务容器 (LivenessProbeStartup.cs) 中进行注册:

services.AddHostedService<StartupHostedService>();
services.AddSingleton<StartupHostedServiceHealthCheck>();

services.AddHealthChecks()
    .AddCheck<StartupHostedServiceHealthCheck>(
        "hosted_service_startup",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "ready" });

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

services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();

通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点。 在示例应用中,在以下位置创建运行状况检查终结点:

  • /health/ready(用于就绪状态检查)。 就绪情况检查会将运行状况检查筛选到具有 ready 标记的运行状况检查。
  • /health/live(用于运行情况检查)。 运行情况检查通过在 HealthCheckOptions.Predicate 中返回 false 来筛选出 StartupHostedServiceHealthCheck(有关详细信息,请参阅筛选运行状况检查

在以下示例代码中:

  • 就绪状态检查将所有已注册的检查与“ready”标记一起使用。
  • Predicate 将排除所有检查并返回 200-Ok。
app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
    {
        Predicate = (check) => check.Tags.Contains("ready"),
    });

    endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
    {
        Predicate = (_) => false
    });
}

若要使用示例应用运行就绪情况/运行情况配置方案,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario liveness

在浏览器中,访问 /health/ready 几次,直到过了 15 秒。 运行状况检查会在前 15 秒内报告 Unhealthy。 15 秒之后,终结点会报告 Healthy,这反映托管服务完成了长时间运行的任务。

此示例还创建了一个运行第一个就绪检查的运行状况检查发布服务器(IHealthCheckPublisher 实现),延迟时间为两秒。 有关详细信息,请参阅运行状况检查发布服务器部分。

Kubernetes 示例

在诸如 Kubernetes 这类环境中,使用单独的就绪情况和运行情况检查会十分有用。 在 Kubernetes 中,应用可能需要在接受请求之前运行耗时的启动工作,如基础数据库可用性测试。 使用单独检查使业务流程协调程序可以区分应用是否正常运行但尚未准备就绪,或是应用程序是否未能启动。 有关 Kubernetes 中的就绪情况和运行情况探测的详细信息,请参阅 Kubernetes 文档中的配置运行情况和就绪情况探测

以下示例演示如何使用 Kubernetes 就绪情况探测配置:

spec:
  template:
  spec:
    readinessProbe:
      # an http probe
      httpGet:
        path: /health/ready
        port: 80
      # length of time to wait for a pod to initialize
      # after pod startup, before applying health checking
      initialDelaySeconds: 30
      timeoutSeconds: 1
    ports:
      - containerPort: 80

具有自定义响应编写器的基于指标的探测

示例应用演示具有自定义响应编写器的内存运行状况检查。

如果应用使用的内存多于给定内存阈值(在示例应用中为 1 GB),则 MemoryHealthCheck 报告降级状态。 HealthCheckResult 包括应用的垃圾回收器 (GC) 信息 (MemoryHealthCheck.cs):

public class MemoryHealthCheck : IHealthCheck
{
    private readonly IOptionsMonitor<MemoryCheckOptions> _options;

    public MemoryHealthCheck(IOptionsMonitor<MemoryCheckOptions> options)
    {
        _options = options;
    }

    public string Name => "memory_check";

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var options = _options.Get(context.Registration.Name);

        // Include GC information in the reported diagnostics.
        var allocated = GC.GetTotalMemory(forceFullCollection: false);
        var data = new Dictionary<string, object>()
        {
            { "AllocatedBytes", allocated },
            { "Gen0Collections", GC.CollectionCount(0) },
            { "Gen1Collections", GC.CollectionCount(1) },
            { "Gen2Collections", GC.CollectionCount(2) },
        };
        var status = (allocated < options.Threshold) ?
            HealthStatus.Healthy : context.Registration.FailureStatus;

        return Task.FromResult(new HealthCheckResult(
            status,
            description: "Reports degraded status if allocated bytes " +
                $">= {options.Threshold} bytes.",
            exception: null,
            data: data));
    }
}

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 MemoryHealthCheck 注册为服务,而不是通过将运行状况检查传递到 AddCheck 来启用它。 所有 IHealthCheck 注册服务都可供运行状况检查服务和中间件使用。 建议将运行状况检查服务注册为单一实例服务。

在示例应用的CustomWriterStartup.cs中:

services.AddHealthChecks()
    .AddMemoryHealthCheck("memory");

通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点。 一个 WriteResponse 委托提供给 ResponseWriter 属性,以在执行运行状况检查时输出自定义 JSON 响应:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        ResponseWriter = WriteResponse
    });
}

WriteResponse 委托将 CompositeHealthCheckResult 格式化为 JSON 对象,并生成运行状况检查响应的 JSON 输出。 有关详细信息,请参阅自定义输出部分。

若要使用示例应用运行具有自定义响应编写器输出的基于指标的探测,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario writer

注意

AspNetCore.Diagnostics.HealthChecks 包括基于指标的运行状况检查方案(包括磁盘存储和最大值运行情况检查)。

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

按端口筛选

使用 URL 模式在 MapHealthChecks 上调用 RequireHost,该 URL 模式指定一个端口,以使运行状况检查请求限于指定端口。 此方法通常用于在容器环境中公开用于监视服务的端口。

示例应用使用环境变量配置提供程序配置端口。 端口在 launchSettings.json 文件设置,并通过环境变量传递到配置提供程序。 还必须配置服务器以在管理端口上侦听请求。

若要使用示例应用演示管理端口配置,请在 Properties 文件夹中创建 launchSettings.json 文件。

示例应用中的以下 Properties/launchSettings.json 文件未包含在示例应用的项目文件中,必须手动创建:

{
  "profiles": {
    "SampleApp": {
      "commandName": "Project",
      "commandLineArgs": "",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_URLS": "http://localhost:5000/;http://localhost:5001/",
        "ASPNETCORE_MANAGEMENTPORT": "5001"
      },
      "applicationUrl": "http://localhost:5000/"
    }
  }
}

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点。

在示例应用中,在 Startup.Configure 中的终结点上调用 RequireHost 将从配置中指定管理端口:

endpoints.MapHealthChecks("/health")
    .RequireHost($"*:{Configuration["ManagementPort"]}");

在示例应用中,在 Startup.Configure 中创建终结点。 在以下示例代码中:

  • 就绪状态检查将所有已注册的检查与“ready”标记一起使用。
  • Predicate 将排除所有检查并返回 200-Ok。
app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
    {
        Predicate = (check) => check.Tags.Contains("ready"),
    });

    endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
    {
        Predicate = (_) => false
    });
}

注意

可以通过在代码中显式设置管理端口,来避免在示例应用中创建 launchSettings.json 文件。 在创建 HostBuilderProgram.cs 中,添加对 ListenAnyIP 的调用并提供应用的管理端口终结点。 在 ManagementPortStartup.csConfigure 中,使用 RequireHost 指定管理端口:

Program.cs:

return new HostBuilder()
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseKestrel()
            .ConfigureKestrel(serverOptions =>
            {
                serverOptions.ListenAnyIP(5001);
            })
            .UseStartup(startupType);
    })
    .Build();

ManagementPortStartup.cs:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health").RequireHost("*:5001");
});

若要使用示例应用运行管理端口配置方案,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario port

分发运行状况检查库

将运行状况检查作为库进行分发:

  1. 编写将 IHealthCheck 接口作为独立类来实现的运行状况检查。 该类可以依赖于依赖关系注入 (DI)类型激活和命名选项来访问配置数据。

    CheckHealthAsync 的运行状况检查逻辑中:

    • 方法中使用 data1data2 运行探测的运行状况检查逻辑。
    • AccessViolationException 已处理。

    发生 AccessViolationException 时,将返回 FailureStatusHealthCheckResult,以允许用户配置运行状况检查失败状态。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Diagnostics.HealthChecks;
    
    namespace SampleApp
    {
        public class ExampleHealthCheck : IHealthCheck
        {
            private readonly string _data1;
            private readonly int? _data2;
    
            public ExampleHealthCheck(string data1, int? data2)
            {
                _data1 = data1 ?? throw new ArgumentNullException(nameof(data1));
                _data2 = data2 ?? throw new ArgumentNullException(nameof(data2));
            }
    
            public async Task<HealthCheckResult> CheckHealthAsync(
                HealthCheckContext context, CancellationToken cancellationToken)
            {
                try
                {
                    return HealthCheckResult.Healthy();
                }
                catch (AccessViolationException ex)
                {
                    return new HealthCheckResult(
                        context.Registration.FailureStatus,
                        description: "An access violation occurred during the check.",
                        exception: ex,
                        data: null);
                }
            }
        }
    }
    
  2. 使用参数编写一个扩展方法,所使用的应用会在其 Startup.Configure 方法中调用它。 在以下示例中,假设以下运行状况检查方法签名:

    ExampleHealthCheck(string, string, int )
    

    前面的签名指示 ExampleHealthCheck 需要其他数据来处理运行状况检查探测逻辑。 当运行状况检查向扩展方法注册时,数据会提供给用于创建运行状况检查实例的委托。 在以下示例中,调用方会指定可选的:

    • 运行状况检查名称 (name)。 如果为 null,则使用 example_health_check
    • 运行状况检查的字符串数据点 (data1)。
    • 运行状况检查的整数数据点 (data2)。 如果为 null,则使用 1
    • 失败状态 (HealthStatus)。 默认值为 null。 如果为 null,则对失败状态报告 HealthStatus.Unhealthy
    • 标记 (IEnumerable<string>)。
    using System.Collections.Generic;
    using Microsoft.Extensions.Diagnostics.HealthChecks;
    
    public static class ExampleHealthCheckBuilderExtensions
    {
        const string DefaultName = "example_health_check";
    
        public static IHealthChecksBuilder AddExampleHealthCheck(
            this IHealthChecksBuilder builder,
            string name = default,
            string data1,
            int data2 = 1,
            HealthStatus? failureStatus = default,
            IEnumerable<string> tags = default)
        {
            return builder.Add(new HealthCheckRegistration(
                name ?? DefaultName,
                sp => new ExampleHealthCheck(data1, data2),
                failureStatus,
                tags));
        }
    }
    

运行状况检查发布服务器

IHealthCheckPublisher 添加到服务容器时,运行状况检查系统,会定期执行运行状况检查并使用结果调用 PublishAsync。 在期望每个进程定期调用监视系统以便确定运行状况的基于推送的运行状况监视系统方案中,这十分有用。

IHealthCheckPublisher 接口具有单个方法:

Task PublishAsync(HealthReport report, CancellationToken cancellationToken);

使用 HealthCheckPublisherOptions 可设置:

  • Delay:在应用启动后且在应用执行 IHealthCheckPublisher 实例之前所应用的初始延迟。 延迟在启动时应用一次,不适用于后续迭代。 默认值为 5 秒。
  • PeriodIHealthCheckPublisher 执行的时间。 默认值为 30 秒。
  • Predicate:如果 Predicatenull(默认值),则运行状况检查发布服务器服务运行所有已注册的运行状况检查。 若要运行运行状况检查的子集,请提供用于筛选检查集的函数。 每个时间段都会评估谓词。
  • Timeout:执行所有 IHealthCheckPublisher 实例的运行状况检查的超时时间。 在不超时的情况下,使用 InfiniteTimeSpan 执行。 默认值为 30 秒。

在示例应用中,ReadinessPublisherIHealthCheckPublisher 实现。 在以下日志级别,针对每次检查记录运行状况检查状态:

public class ReadinessPublisher : IHealthCheckPublisher
{
    private readonly ILogger _logger;

    public ReadinessPublisher(ILogger<ReadinessPublisher> logger)
    {
        _logger = logger;
    }

    // The following example is for demonstration purposes only. Health Checks
    // Middleware already logs health checks results. A real-world readiness
    // check in a production app might perform a set of more expensive or
    // time-consuming checks to determine if other resources are responding
    // properly.
    public Task PublishAsync(HealthReport report,
        CancellationToken cancellationToken)
    {
        if (report.Status == HealthStatus.Healthy)
        {
            _logger.LogInformation("{Timestamp} Readiness Probe Status: {Result}",
                DateTime.UtcNow, report.Status);
        }
        else
        {
            _logger.LogError("{Timestamp} Readiness Probe Status: {Result}",
                DateTime.UtcNow, report.Status);
        }

        cancellationToken.ThrowIfCancellationRequested();

        return Task.CompletedTask;
    }
}

在示例应用的 LivenessProbeStartup 示例中,StartupHostedService 就绪状态检查有两秒的启动延迟,并且每 30 秒运行一次检查。 为激活 IHealthCheckPublisher 实现,示例将 ReadinessPublisher 注册为依存关系注入 (DI) 容器中的单一实例服务:

services.AddHostedService<StartupHostedService>();
services.AddSingleton<StartupHostedServiceHealthCheck>();

services.AddHealthChecks()
    .AddCheck<StartupHostedServiceHealthCheck>(
        "hosted_service_startup",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "ready" });

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

services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();

注意

AspNetCore.Diagnostics.HealthChecks 包括多个系统的发布服务器(包括 Application Insights)。

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

使用 MapWhen 限制运行状况检查

使用 MapWhen 对运行状况检查终结点的请求管道进行条件分支。

在以下示例中,如果收到 api/HealthCheck 终结点的 GET 请求,MapWhen 将对请求管道进行分支以激活运行状况检查中间件:

app.MapWhen(
    context => context.Request.Method == HttpMethod.Get.Method && 
        context.Request.Path.StartsWith("/api/HealthCheck"),
    builder => builder.UseHealthChecks());

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
});

有关详细信息,请参阅 ASP.NET Core 中间件

ASP.NET Core 提供运行状况检查中间件和库,以用于报告应用基础结构组件的运行状况。

运行状况检查由应用程序作为 HTTP 终结点公开。 可以为各种实时监视方案配置运行状况检查终结点:

  • 运行状况探测可以由容器业务流程协调程和负载均衡器用于检查应用的状态。 例如,容器业务流程协调程序可以通过停止滚动部署或重新启动容器来响应失败的运行状况检查。 负载均衡器可以通过将流量从失败的实例路由到正常实例,来应对不正常的应用。
  • 可以监视内存、磁盘和其他物理服务器资源的使用情况来了解是否处于正常状态。
  • 运行状况检查可以测试应用的依赖项(如数据库和外部服务终结点)以确认是否可用和正常工作。

查看或下载示例代码如何下载

示例应用包含本文中所述的方案示例。 若要运行给定方案的示例应用,请在命令行界面中从项目文件夹中使用 dotnet run 命令。 请参阅示例应用的 README.md 文件和本文中的方案说明,详细了解如何使用示例应用。

先决条件

运行状况检查通常与外部监视服务或容器业务流程协调程序一起用于检查应用的状态。 向应用添加运行状况检查之前,需确定要使用的监视系统。 监视系统决定了要创建的运行状况检查类型以及配置其终结点的方式。

ASP.NET Core 应用隐式引用 Microsoft.AspNetCore.Diagnostics.HealthChecks 包。 若要使用 Entity Framework Core 运行运行状况检查,请添加对 Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 包的包引用。

示例应用提供了启动代码来演示几个方案的运行状况检查。 数据库探测方案使用 AspNetCore.Diagnostics.HealthChecks 检查数据库连接的运行状况。 DbContext 探测方案使用 EF CoreDbContext 检查数据库。 若要探索数据库方案,示例应用将:

注意

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

另一个运行状况检查方案演示如何将运行状况检查筛选到某个管理端口。 示例应用要求创建包含管理 URL 和管理端口的 Properties/launchSettings.json 文件。 有关详细信息,请参阅按端口筛选部分。

基本运行状况探测

对于许多应用,报告应用在处理请求方面的可用性(运行情况)的基本运行状况探测配置足以发现应用的状态。

基本配置会注册运行状况检查服务,并调用运行状况检查中间件以通过运行状况响应在 URL 终结点处进行响应。 默认情况下,不会注册任何特定运行状况检查来测试任何特定依赖项或子系统。 如果能够在运行状况终结点 URL 处进行响应,则应用被视为正常。 默认响应编写器会以纯文本响应形式将状态 (HealthStatus) 写回到客户端,以便指示 HealthStatus.HealthyHealthStatus.DegradedHealthStatus.Unhealthy 状态。

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点。

在示例应用中,在 /health 处创建运行状况检查终结点 (BasicStartup.cs):

public class BasicStartup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHealthChecks();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHealthChecks("/health");
        });
    }
}

若要使用示例应用运行基本配置方案,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario basic

Docker 示例

Docker 提供内置 HEALTHCHECK 指令,该指令可以用于检查使用基本运行状况检查配置的应用的状态:

HEALTHCHECK CMD curl --fail http://localhost:5000/health || exit

创建运行状况检查

运行状况检查通过实现 IHealthCheck 接口进行创建。 CheckHealthAsync 方法会返回 HealthCheckResult,它以 HealthyDegradedUnhealthy 的形式指示运行状况。 结果会使用可配置状态代码(配置在运行状况检查选项部分中进行介绍)编写为纯文本响应。 HealthCheckResult 还可以返回可选的键值对。

下面的 ExampleHealthCheck 类演示运行状况检查的布局。 运行状况检查逻辑位于 CheckHealthAsync 方法中。 以下示例将虚拟变量 healthCheckResultHealthy 设为 true。 如果 healthCheckResultHealthy 的值设为 false,则返回 HealthCheckResult.Unhealthy 状态。

public class ExampleHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var healthCheckResultHealthy = true;

        if (healthCheckResultHealthy)
        {
            return Task.FromResult(
                HealthCheckResult.Healthy("A healthy result."));
        }

        return Task.FromResult(
            HealthCheckResult.Unhealthy("An unhealthy result."));
    }
}

注册运行状况检查服务

ExampleHealthCheck 类型使用 Startup.ConfigureServices 中的 AddCheck 添加到运行状况检查服务:

services.AddHealthChecks()
    .AddCheck<ExampleHealthCheck>("example_health_check");

以下示例中显示的 AddCheck 重载会设置要在运行状况检查报告失败时报告的失败状态 (HealthStatus)。 如果失败状态设置为 null(默认值),则会报告 HealthStatus.Unhealthy。 此重载对于库创建者是一种十分有用的方案,在这种情况下,如果运行状况检查实现遵循该设置,则在发生运行状况检查失败时,应用会强制实施库所指示的失败状态。

标记用于筛选运行状况检查(在筛选运行状况检查部分中进行了进一步介绍)。

services.AddHealthChecks()
    .AddCheck<ExampleHealthCheck>(
        "example_health_check",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "example" });

AddCheck 还可以执行 lambda 函数。 在以下示例中,运行状况检查名称指定为 Example,并且检查始终返回正常状态:

services.AddHealthChecks()
    .AddCheck("Example", () =>
        HealthCheckResult.Healthy("Example is OK!"), tags: new[] { "example" });

调用 AddTypeActivatedCheck 将参数传递到运行状况检查实现。 在以下示例中,TestHealthCheckWithArgs 接受一个整数和一个字符串,以便在调用 CheckHealthAsync 时使用:

private class TestHealthCheckWithArgs : IHealthCheck
{
    public TestHealthCheckWithArgs(int i, string s)
    {
        I = i;
        S = s;
    }

    public int I { get; set; }

    public string S { get; set; }

    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, 
        CancellationToken cancellationToken = default)
    {
        ...
    }
}

TestHealthCheckWithArgs 通过使用传递到实现的整数和字符串调用 AddTypeActivatedCheck 来注册:

services.AddHealthChecks()
    .AddTypeActivatedCheck<TestHealthCheckWithArgs>(
        "test", 
        failureStatus: HealthStatus.Degraded, 
        tags: new[] { "example" }, 
        args: new object[] { 5, "string" });

使用运行状况检查路由

Startup.Configure 内,使用终结点 URL 或相对路径在终结点生成器上调用 MapHealthChecks

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health");
});

需要主机

调用 RequireHost 以便为运行状况检查终结点指定一个或多个允许的主机。 主机应为 Unicode 而不是 punycode,且可以包含端口。 如果未提供集合,则接受任何主机。

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health").RequireHost("www.contoso.com:5001");
});

有关详细信息,请参阅按端口筛选部分。

需要授权

调用 RequireAuthorization 以在状况检查请求终结点上运行身份验证中间件。 RequireAuthorization 重载接受一个或多个授权策略。 如果未提供策略,则使用默认的授权策略。

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health").RequireAuthorization();
});

启用跨域请求 (CORS)

尽管从浏览器手动运行运行状况检查不是常见的使用方案,但可以通过在运行状况检查终结点上调用 RequireCors 来启用 CORS 中间件。 RequireCors 重载接受 CORS 策略生成器委托 (CorsPolicyBuilder) 或策略名称。 如果未提供策略,则使用默认的 CORS 策略。 有关详细信息,请参阅在 ASP.NET Core 中启用跨源请求 (CORS)

运行状况检查选项

HealthCheckOptions 使你可以自定义运行状况检查行为:

筛选运行状况检查

默认情况下,运行状况检查中间件会运行所有已注册的运行状况检查。 若要运行运行状况检查的子集,请提供向 Predicate 选项返回布尔值的函数。 在以下示例中,Bar 运行状况检查在函数条件语句 中由于其标记 (bar_tag) 而被筛选掉,在条件语句中,仅当运行状况检查的 Tags 属性与 foo_tagbaz_tag 匹配时才返回 true

Startup.ConfigureServices中:

services.AddHealthChecks()
    .AddCheck("Foo", () =>
        HealthCheckResult.Healthy("Foo is OK!"), tags: new[] { "foo_tag" })
    .AddCheck("Bar", () =>
        HealthCheckResult.Unhealthy("Bar is unhealthy!"), tags: new[] { "bar_tag" })
    .AddCheck("Baz", () =>
        HealthCheckResult.Healthy("Baz is OK!"), tags: new[] { "baz_tag" });

Startup.Configure 中,Predicate 筛选出“Bar”运行状况检查。 仅 Foo 和 Baz 执行:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        Predicate = (check) => check.Tags.Contains("foo_tag") ||
            check.Tags.Contains("baz_tag")
    });
});

自定义 HTTP 状态代码

使用 ResultStatusCodes 可自定义运行状况状态到 HTTP 状态代码的映射。 以下 StatusCodes 分配是中间件所使用的默认值。 更改状态代码值以满足要求。

Startup.Configure中:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        ResultStatusCodes =
        {
            [HealthStatus.Healthy] = StatusCodes.Status200OK,
            [HealthStatus.Degraded] = StatusCodes.Status200OK,
            [HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
        }
    });
});

取消缓存标头

AllowCachingResponses 控制运行状况检查中间件是否将 HTTP 标头添加到探测响应以防止响应缓存。 如果值为 false(默认值),则中间件会设置或替代 Cache-ControlExpiresPragma 标头以防止响应缓存。 如果值为 true,则中间件不会修改响应的缓存标头。

Startup.Configure中:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        AllowCachingResponses = false
    });
});

自定义输出

Startup.Configure 中,将 HealthCheckOptions.ResponseWriter 选项设置为编写响应的委托:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        ResponseWriter = WriteResponse
    });
});

默认委托会使用 HealthReport.Status 字符串值编写最小的纯文本响应。 以下自定义委托输出自定义 JSON 响应。

示例应用中的第一个示例演示如何使用 System.Text.Json

private static Task WriteResponse(HttpContext context, HealthReport result)
{
    context.Response.ContentType = "application/json; charset=utf-8";

    var options = new JsonWriterOptions
    {
        Indented = true
    };

    using (var stream = new MemoryStream())
    {
        using (var writer = new Utf8JsonWriter(stream, options))
        {
            writer.WriteStartObject();
            writer.WriteString("status", result.Status.ToString());
            writer.WriteStartObject("results");
            foreach (var entry in result.Entries)
            {
                writer.WriteStartObject(entry.Key);
                writer.WriteString("status", entry.Value.Status.ToString());
                writer.WriteString("description", entry.Value.Description);
                writer.WriteStartObject("data");
                foreach (var item in entry.Value.Data)
                {
                    writer.WritePropertyName(item.Key);
                    JsonSerializer.Serialize(
                        writer, item.Value, item.Value?.GetType() ??
                        typeof(object));
                }
                writer.WriteEndObject();
                writer.WriteEndObject();
            }
            writer.WriteEndObject();
            writer.WriteEndObject();
        }

        var json = Encoding.UTF8.GetString(stream.ToArray());

        return context.Response.WriteAsync(json);
    }
}

第二个示例演示如何使用 Newtonsoft.Json

private static Task WriteResponse(HttpContext context, HealthReport result)
{
    context.Response.ContentType = "application/json";

    var json = new JObject(
        new JProperty("status", result.Status.ToString()),
        new JProperty("results", new JObject(result.Entries.Select(pair =>
            new JProperty(pair.Key, new JObject(
                new JProperty("status", pair.Value.Status.ToString()),
                new JProperty("description", pair.Value.Description),
                new JProperty("data", new JObject(pair.Value.Data.Select(
                    p => new JProperty(p.Key, p.Value))))))))));

    return context.Response.WriteAsync(
        json.ToString(Formatting.Indented));
}

在示例应用中,注释掉 CustomWriterStartup.cs 中的 SYSTEM_TEXT_JSON预处理器指令,以启用 WriteResponseNewtonsoft.Json 版本。

运行状况检查 API 不为复杂 JSON 返回格式提供内置支持,因为该格式特定于你选择的监视系统。 必要时自定义上述示例中的响应。 有关使用 System.Text.Json 执行 JSON 序列化的详细信息,请参阅如何在 .NET 中序列化和反序列化 JSON

数据库探测

运行状况检查可以指定数据库查询作为布尔测试来运行,以指示数据库是否在正常响应。

示例应用使用 AspNetCore.Diagnostics.HealthChecks(适用于 ASP.NET Core 应用的运行状况检查库)对 SQL Server 数据库运行运行状况检查。 AspNetCore.Diagnostics.HealthChecks 对数据库执行 SELECT 1 查询以确认与数据库的连接是否正常。

警告

使用查询检查数据库连接时,请选择快速返回的查询。 查询方法会面临使数据库过载和降低其性能的风险。 在大多数情况下,无需运行测试查询。 只需建立成功的数据库连接便足矣。 如果发现需要运行查询,请选择简单的 SELECT 查询,如 SELECT 1

添加对 AspNetCore.HealthChecks.SqlServer 的包引用。

在示例应用的 appsettings.json 文件中提供有效数据库连接字符串。 应用使用名为 HealthCheckSample 的 SQL Server 数据库:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=HealthCheckSample;Trusted_Connection=True;MultipleActiveResultSets=true;ConnectRetryCount=0"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    },
    "Console": {
      "IncludeScopes": "true"
    }
  },
  "AllowedHosts": "*"
}

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 示例应用使用数据库的连接字符串 (DbHealthStartup.cs) 调用 AddSqlServer 方法:

services.AddHealthChecks()
    .AddSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);

通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health");
}

若要使用示例应用运行数据库探测方案,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario db

注意

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

Entity Framework Core DbContext 探测

DbContext 检查确认应用可以与为 EF CoreDbContext 配置的数据库通信。 满足以下条件的应用支持 DbContext 检查:

AddDbContextCheck<TContext>DbContext 注册运行状况检查。 DbContext 作为方法的 TContext 提供。 重载可用于配置失败状态、标记和自定义测试查询。

默认情况下:

  • DbContextHealthCheck 调用 EF Core 的 CanConnectAsync 方法。 可以自定义在使用 AddDbContextCheck 方法重载检查运行状况时运行的操作。
  • 运行状况检查的名称是 TContext 类型的名称。

在示例应用中,AppDbContext 会提供给 AddDbContextCheck,并在 Startup.ConfigureServices 中注册为服务 (DbContextHealthStartup.cs)。

services.AddHealthChecks()
    .AddDbContextCheck<AppDbContext>();

services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(
        Configuration["ConnectionStrings:DefaultConnection"]);
});

通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health");
}

若要使用示例应用运行 DbContext 探测方案,请确认连接字符串指定的数据库在 SQL Server 实例中不存在。 如果该数据库存在,请删除它。

在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario dbcontext

在应用运行之后,在浏览器中对 /health 终结点发出请求,从而检查运行状况。 数据库和 AppDbContext 不存在,因此应用提供以下响应:

Unhealthy

触发示例应用以创建数据库。 向 /createdatabase 发出请求。 应用会进行以下响应:

Creating the database...
Done!
Navigate to /health to see the health status.

/health 终结点发出请求。 数据库和上下文存在,因此应用会进行以下响应:

Healthy

触发示例应用以删除数据库。 向 /deletedatabase 发出请求。 应用会进行以下响应:

Deleting the database...
Done!
Navigate to /health to see the health status.

/health 终结点发出请求。 应用提供不正常的响应:

Unhealthy

单独的就绪情况和运行情况探测

在某些托管方案中,使用一对运行状况检查来区分两种应用状态:

  • “就绪情况”指示情况是否为应用正常运行,但未准备好接收请求。
  • “运行情况”指示情况是否为应用已崩溃且必须重启。

请看下面的示例:应用必须下载大型配置文件才能处理请求。 如果初始下载失败,我们不希望重启该应用,因为该应用可以多次尝试下载该文件。 我们使用运行情况探测来描述进程的运行情况,不运行其他检查。 我们还想要在配置文件下载成功之前阻止将请求发送到应用。 我们使用就绪情况探测指示“未就绪”状态,直到下载成功并且应用已准备好接收请求。

示例应用包含运行状况检查,以报告托管服务中长时间运行的启动任务的完成。 StartupHostedServiceHealthCheck 公开了属性 StartupTaskCompleted,托管服务在其长时间运行的任务完成时可以将该属性设置为 true (StartupHostedServiceHealthCheck.cs):

public class StartupHostedServiceHealthCheck : IHealthCheck
{
    private volatile bool _startupTaskCompleted = false;

    public string Name => "slow_dependency_check";

    public bool StartupTaskCompleted
    {
        get => _startupTaskCompleted;
        set => _startupTaskCompleted = value;
    }

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        if (StartupTaskCompleted)
        {
            return Task.FromResult(
                HealthCheckResult.Healthy("The startup task is finished."));
        }

        return Task.FromResult(
            HealthCheckResult.Unhealthy("The startup task is still running."));
    }
}

长时间运行的后台任务由托管服务 (Services/StartupHostedService) 启动。 在该任务结束时,StartupHostedServiceHealthCheck.StartupTaskCompleted 设置为 true

public class StartupHostedService : IHostedService, IDisposable
{
    private readonly int _delaySeconds = 15;
    private readonly ILogger _logger;
    private readonly StartupHostedServiceHealthCheck _startupHostedServiceHealthCheck;

    public StartupHostedService(ILogger<StartupHostedService> logger,
        StartupHostedServiceHealthCheck startupHostedServiceHealthCheck)
    {
        _logger = logger;
        _startupHostedServiceHealthCheck = startupHostedServiceHealthCheck;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Startup Background Service is starting.");

        // Simulate the effect of a long-running startup task.
        Task.Run(async () =>
        {
            await Task.Delay(_delaySeconds * 1000);

            _startupHostedServiceHealthCheck.StartupTaskCompleted = true;

            _logger.LogInformation("Startup Background Service has started.");
        });

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Startup Background Service is stopping.");

        return Task.CompletedTask;
    }

    public void Dispose()
    {
    }
}

运行状况检查在 Startup.ConfigureServices 中使用 AddCheck 与托管服务一起注册。 因为托管服务必须对运行状况检查设置该属性,所以运行状况检查也会在服务容器 (LivenessProbeStartup.cs) 中进行注册:

services.AddHostedService<StartupHostedService>();
services.AddSingleton<StartupHostedServiceHealthCheck>();

services.AddHealthChecks()
    .AddCheck<StartupHostedServiceHealthCheck>(
        "hosted_service_startup",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "ready" });

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

services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();

通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点。 在示例应用中,在以下位置创建运行状况检查终结点:

  • /health/ready(用于就绪状态检查)。 就绪情况检查会将运行状况检查筛选到具有 ready 标记的运行状况检查。
  • /health/live(用于运行情况检查)。 运行情况检查通过在 HealthCheckOptions.Predicate 中返回 false 来筛选出 StartupHostedServiceHealthCheck(有关详细信息,请参阅筛选运行状况检查

在以下示例代码中:

  • 就绪状态检查将所有已注册的检查与“ready”标记一起使用。
  • Predicate 将排除所有检查并返回 200-Ok。
app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
    {
        Predicate = (check) => check.Tags.Contains("ready"),
    });

    endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
    {
        Predicate = (_) => false
    });
}

若要使用示例应用运行就绪情况/运行情况配置方案,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario liveness

在浏览器中,访问 /health/ready 几次,直到过了 15 秒。 运行状况检查会在前 15 秒内报告 Unhealthy。 15 秒之后,终结点会报告 Healthy,这反映托管服务完成了长时间运行的任务。

此示例还创建了一个运行第一个就绪检查的运行状况检查发布服务器(IHealthCheckPublisher 实现),延迟时间为两秒。 有关详细信息,请参阅运行状况检查发布服务器部分。

Kubernetes 示例

在诸如 Kubernetes 这类环境中,使用单独的就绪情况和运行情况检查会十分有用。 在 Kubernetes 中,应用可能需要在接受请求之前运行耗时的启动工作,如基础数据库可用性测试。 使用单独检查使业务流程协调程序可以区分应用是否正常运行但尚未准备就绪,或是应用程序是否未能启动。 有关 Kubernetes 中的就绪情况和运行情况探测的详细信息,请参阅 Kubernetes 文档中的配置运行情况和就绪情况探测

以下示例演示如何使用 Kubernetes 就绪情况探测配置:

spec:
  template:
  spec:
    readinessProbe:
      # an http probe
      httpGet:
        path: /health/ready
        port: 80
      # length of time to wait for a pod to initialize
      # after pod startup, before applying health checking
      initialDelaySeconds: 30
      timeoutSeconds: 1
    ports:
      - containerPort: 80

具有自定义响应编写器的基于指标的探测

示例应用演示具有自定义响应编写器的内存运行状况检查。

如果应用使用的内存多于给定内存阈值(在示例应用中为 1 GB),则 MemoryHealthCheck 报告降级状态。 HealthCheckResult 包括应用的垃圾回收器 (GC) 信息 (MemoryHealthCheck.cs):

public class MemoryHealthCheck : IHealthCheck
{
    private readonly IOptionsMonitor<MemoryCheckOptions> _options;

    public MemoryHealthCheck(IOptionsMonitor<MemoryCheckOptions> options)
    {
        _options = options;
    }

    public string Name => "memory_check";

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var options = _options.Get(context.Registration.Name);

        // Include GC information in the reported diagnostics.
        var allocated = GC.GetTotalMemory(forceFullCollection: false);
        var data = new Dictionary<string, object>()
        {
            { "AllocatedBytes", allocated },
            { "Gen0Collections", GC.CollectionCount(0) },
            { "Gen1Collections", GC.CollectionCount(1) },
            { "Gen2Collections", GC.CollectionCount(2) },
        };
        var status = (allocated < options.Threshold) ?
            HealthStatus.Healthy : context.Registration.FailureStatus;

        return Task.FromResult(new HealthCheckResult(
            status,
            description: "Reports degraded status if allocated bytes " +
                $">= {options.Threshold} bytes.",
            exception: null,
            data: data));
    }
}

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 MemoryHealthCheck 注册为服务,而不是通过将运行状况检查传递到 AddCheck 来启用它。 所有 IHealthCheck 注册服务都可供运行状况检查服务和中间件使用。 建议将运行状况检查服务注册为单一实例服务。

在示例应用的CustomWriterStartup.cs中:

services.AddHealthChecks()
    .AddMemoryHealthCheck("memory");

通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点。 一个 WriteResponse 委托提供给 ResponseWriter 属性,以在执行运行状况检查时输出自定义 JSON 响应:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
    {
        ResponseWriter = WriteResponse
    });
}

WriteResponse 委托将 CompositeHealthCheckResult 格式化为 JSON 对象,并生成运行状况检查响应的 JSON 输出。 有关详细信息,请参阅自定义输出部分。

若要使用示例应用运行具有自定义响应编写器输出的基于指标的探测,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario writer

注意

AspNetCore.Diagnostics.HealthChecks 包括基于指标的运行状况检查方案(包括磁盘存储和最大值运行情况检查)。

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

按端口筛选

使用 URL 模式在 MapHealthChecks 上调用 RequireHost,该 URL 模式指定一个端口,以使运行状况检查请求限于指定端口。 此方法通常用于在容器环境中公开用于监视服务的端口。

示例应用使用环境变量配置提供程序配置端口。 端口在 launchSettings.json 文件设置,并通过环境变量传递到配置提供程序。 还必须配置服务器以在管理端口上侦听请求。

若要使用示例应用演示管理端口配置,请在 Properties 文件夹中创建 launchSettings.json 文件。

示例应用中的以下 Properties/launchSettings.json 文件未包含在示例应用的项目文件中,必须手动创建:

{
  "profiles": {
    "SampleApp": {
      "commandName": "Project",
      "commandLineArgs": "",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_URLS": "http://localhost:5000/;http://localhost:5001/",
        "ASPNETCORE_MANAGEMENTPORT": "5001"
      },
      "applicationUrl": "http://localhost:5000/"
    }
  }
}

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 通过在 Startup.Configure 中调用 MapHealthChecks 来创建运行状况检查终结点。

在示例应用中,在 Startup.Configure 中的终结点上调用 RequireHost 将从配置中指定管理端口:

endpoints.MapHealthChecks("/health")
    .RequireHost($"*:{Configuration["ManagementPort"]}");

在示例应用中,在 Startup.Configure 中创建终结点。 在以下示例代码中:

  • 就绪状态检查将所有已注册的检查与“ready”标记一起使用。
  • Predicate 将排除所有检查并返回 200-Ok。
app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
    {
        Predicate = (check) => check.Tags.Contains("ready"),
    });

    endpoints.MapHealthChecks("/health/live", new HealthCheckOptions()
    {
        Predicate = (_) => false
    });
}

注意

可以通过在代码中显式设置管理端口,来避免在示例应用中创建 launchSettings.json 文件。 在创建 HostBuilderProgram.cs 中,添加对 ListenAnyIP 的调用并提供应用的管理端口终结点。 在 ManagementPortStartup.csConfigure 中,使用 RequireHost 指定管理端口:

Program.cs:

return new HostBuilder()
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseKestrel()
            .ConfigureKestrel(serverOptions =>
            {
                serverOptions.ListenAnyIP(5001);
            })
            .UseStartup(startupType);
    })
    .Build();

ManagementPortStartup.cs:

app.UseEndpoints(endpoints =>
{
    endpoints.MapHealthChecks("/health").RequireHost("*:5001");
});

若要使用示例应用运行管理端口配置方案,请在命令行界面中从项目文件夹执行以下命令:

dotnet run --scenario port

分发运行状况检查库

将运行状况检查作为库进行分发:

  1. 编写将 IHealthCheck 接口作为独立类来实现的运行状况检查。 该类可以依赖于依赖关系注入 (DI)类型激活和命名选项来访问配置数据。

    CheckHealthAsync 的运行状况检查逻辑中:

    • 方法中使用 data1data2 运行探测的运行状况检查逻辑。
    • AccessViolationException 已处理。

    发生 AccessViolationException 时,将返回 FailureStatusHealthCheckResult,以允许用户配置运行状况检查失败状态。

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Extensions.Diagnostics.HealthChecks;
    
    namespace SampleApp
    {
        public class ExampleHealthCheck : IHealthCheck
        {
            private readonly string _data1;
            private readonly int? _data2;
    
            public ExampleHealthCheck(string data1, int? data2)
            {
                _data1 = data1 ?? throw new ArgumentNullException(nameof(data1));
                _data2 = data2 ?? throw new ArgumentNullException(nameof(data2));
            }
    
            public async Task<HealthCheckResult> CheckHealthAsync(
                HealthCheckContext context, CancellationToken cancellationToken)
            {
                try
                {
                    return HealthCheckResult.Healthy();
                }
                catch (AccessViolationException ex)
                {
                    return new HealthCheckResult(
                        context.Registration.FailureStatus,
                        description: "An access violation occurred during the check.",
                        exception: ex,
                        data: null);
                }
            }
        }
    }
    
  2. 使用参数编写一个扩展方法,所使用的应用会在其 Startup.Configure 方法中调用它。 在以下示例中,假设以下运行状况检查方法签名:

    ExampleHealthCheck(string, string, int )
    

    前面的签名指示 ExampleHealthCheck 需要其他数据来处理运行状况检查探测逻辑。 当运行状况检查向扩展方法注册时,数据会提供给用于创建运行状况检查实例的委托。 在以下示例中,调用方会指定可选的:

    • 运行状况检查名称 (name)。 如果为 null,则使用 example_health_check
    • 运行状况检查的字符串数据点 (data1)。
    • 运行状况检查的整数数据点 (data2)。 如果为 null,则使用 1
    • 失败状态 (HealthStatus)。 默认值为 null。 如果为 null,则对失败状态报告 HealthStatus.Unhealthy
    • 标记 (IEnumerable<string>)。
    using System.Collections.Generic;
    using Microsoft.Extensions.Diagnostics.HealthChecks;
    
    public static class ExampleHealthCheckBuilderExtensions
    {
        const string DefaultName = "example_health_check";
    
        public static IHealthChecksBuilder AddExampleHealthCheck(
            this IHealthChecksBuilder builder,
            string name = default,
            string data1,
            int data2 = 1,
            HealthStatus? failureStatus = default,
            IEnumerable<string> tags = default)
        {
            return builder.Add(new HealthCheckRegistration(
                name ?? DefaultName,
                sp => new ExampleHealthCheck(data1, data2),
                failureStatus,
                tags));
        }
    }
    

运行状况检查发布服务器

IHealthCheckPublisher 添加到服务容器时,运行状况检查系统,会定期执行运行状况检查并使用结果调用 PublishAsync。 在期望每个进程定期调用监视系统以便确定运行状况的基于推送的运行状况监视系统方案中,这十分有用。

IHealthCheckPublisher 接口具有单个方法:

Task PublishAsync(HealthReport report, CancellationToken cancellationToken);

使用 HealthCheckPublisherOptions 可设置:

  • Delay:在应用启动后且在应用执行 IHealthCheckPublisher 实例之前所应用的初始延迟。 延迟在启动时应用一次,不适用于后续迭代。 默认值为 5 秒。
  • PeriodIHealthCheckPublisher 执行的时间。 默认值为 30 秒。
  • Predicate:如果 Predicatenull(默认值),则运行状况检查发布服务器服务运行所有已注册的运行状况检查。 若要运行运行状况检查的子集,请提供用于筛选检查集的函数。 每个时间段都会评估谓词。
  • Timeout:执行所有 IHealthCheckPublisher 实例的运行状况检查的超时时间。 在不超时的情况下,使用 InfiniteTimeSpan 执行。 默认值为 30 秒。

在示例应用中,ReadinessPublisherIHealthCheckPublisher 实现。 在以下日志级别,针对每次检查记录运行状况检查状态:

public class ReadinessPublisher : IHealthCheckPublisher
{
    private readonly ILogger _logger;

    public ReadinessPublisher(ILogger<ReadinessPublisher> logger)
    {
        _logger = logger;
    }

    // The following example is for demonstration purposes only. Health Checks
    // Middleware already logs health checks results. A real-world readiness
    // check in a production app might perform a set of more expensive or
    // time-consuming checks to determine if other resources are responding
    // properly.
    public Task PublishAsync(HealthReport report,
        CancellationToken cancellationToken)
    {
        if (report.Status == HealthStatus.Healthy)
        {
            _logger.LogInformation("{Timestamp} Readiness Probe Status: {Result}",
                DateTime.UtcNow, report.Status);
        }
        else
        {
            _logger.LogError("{Timestamp} Readiness Probe Status: {Result}",
                DateTime.UtcNow, report.Status);
        }

        cancellationToken.ThrowIfCancellationRequested();

        return Task.CompletedTask;
    }
}

在示例应用的 LivenessProbeStartup 示例中,StartupHostedService 就绪状态检查有两秒的启动延迟,并且每 30 秒运行一次检查。 为激活 IHealthCheckPublisher 实现,示例将 ReadinessPublisher 注册为依存关系注入 (DI) 容器中的单一实例服务:

services.AddHostedService<StartupHostedService>();
services.AddSingleton<StartupHostedServiceHealthCheck>();

services.AddHealthChecks()
    .AddCheck<StartupHostedServiceHealthCheck>(
        "hosted_service_startup",
        failureStatus: HealthStatus.Degraded,
        tags: new[] { "ready" });

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

services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();

注意

AspNetCore.Diagnostics.HealthChecks 包括多个系统的发布服务器(包括 Application Insights)。

AspNetCore.Diagnostics.HealthChecks 不由 Microsoft 进行支持或维护。

使用 MapWhen 限制运行状况检查

使用 MapWhen 对运行状况检查终结点的请求管道进行条件分支。

在以下示例中,如果收到 api/HealthCheck 终结点的 GET 请求,MapWhen 将对请求管道进行分支以激活运行状况检查中间件:

app.MapWhen(
    context => context.Request.Method == HttpMethod.Get.Method && 
        context.Request.Path.StartsWith("/api/HealthCheck"),
    builder => builder.UseHealthChecks());

app.UseEndpoints(endpoints =>
{
    endpoints.MapRazorPages();
});

有关详细信息,请参阅 ASP.NET Core 中间件