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

通过 Luke LathamGlenn Condron

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 Core DbContext 检查数据库。 若要探索数据库方案,示例应用将:

备注

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 Core DbContext 配置的数据库通信。 满足以下条件的应用支持 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 秒内报告“运行不正常” 。 15 秒之后,终结点会报告“运行正常” ,这反映托管服务完成了长时间运行的任务。

此示例还创建了一个运行第一个就绪检查的运行状况检查发布服务器(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 委托提供给 <Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions.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 文件。 在创建 HostBuilder 的 Program.cs 中,添加对 ListenAnyIP 的调用并提供应用的管理端口终结点。 在 ManagementPortStartup.cs 的 Configure 中,使用 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 文件和本主题中的方案说明,以了解有关如何使用示例应用的详细信息。

先决条件

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

引用 Microsoft.AspNetCore.App 元包或将包引用添加到 Microsoft.AspNetCore.Diagnostics.HealthChecks 包。

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

备注

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

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

基本运行状况探测

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

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

Startup.ConfigureServices 中使用 AddHealthChecks 注册运行状况检查服务。 Startup.Configure 的请求处理管道中,使用 UseHealthChecks 为运行状况检查中间件添加终结点。

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

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

    public void Configure(IApplicationBuilder app)
    {
        app.UseHealthChecks("/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("The check indicates a healthy result."));
        }

        return Task.FromResult(
            HealthCheckResult.Unhealthy("The check indicates 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 函数。 在以下 Startup.ConfigureServices 示例中,运行状况检查名称指定为 Example,并且检查始终返回正常状态:

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

使用运行状况检查中间件

Startup.Configure 内,在处理管道中使用终结点 URL 或相对路径调用 UseHealthChecks

app.UseHealthChecks("/health");

如果运行状况检查应侦听特定端口,则使用 UseHealthChecks 的重载设置端口(在按端口筛选部分中进行了进一步介绍):

app.UseHealthChecks("/health", port: 8000);

运行状况检查选项

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

筛选运行状况检查

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

using System.Threading.Tasks;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.Diagnostics.HealthChecks;

public void ConfigureServices(IServiceCollection services)
{
    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" });
}

public void Configure(IApplicationBuilder app)
{
    app.UseHealthChecks("/health", new HealthCheckOptions()
    {
        Predicate = (check) => check.Tags.Contains("foo_tag") ||
            check.Tags.Contains("baz_tag")
    });
}

自定义 HTTP 状态代码

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

Startup.Configure中:

//using Microsoft.AspNetCore.Diagnostics.HealthChecks;
//using Microsoft.Extensions.Diagnostics.HealthChecks;

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

取消缓存标头

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

Startup.Configure中:

//using Microsoft.AspNetCore.Diagnostics.HealthChecks;
//using Microsoft.Extensions.Diagnostics.HealthChecks;

app.UseHealthChecks("/health", new HealthCheckOptions()
{
    AllowCachingResponses = false
});

自定义输出

ResponseWriter 选项可获取或设置用于编写响应的委托。 默认委托会使用 HealthReport.Status 字符串值编写最小的纯文本响应。

Startup.Configure中:

// using Microsoft.AspNetCore.Diagnostics.HealthChecks;
// using Microsoft.Extensions.Diagnostics.HealthChecks;

app.UseHealthChecks("/health", new HealthCheckOptions()
{
    ResponseWriter = WriteResponse
});

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

private static Task WriteResponse(HttpContext httpContext, HealthReport result)
{
    httpContext.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 httpContext.Response.WriteAsync(
        json.ToString(Formatting.Indented));
}

运行状况检查系统不为复杂 JSON 返回格式提供内置支持,因为该格式特定于你选择的监视系统。 可以根据需要在前面的示例中随意自定义 JObject 以满足你的需求。

数据库探测

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

示例应用使用 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": "Debug"
    },
    "Console": {
      "IncludeScopes": "true"
    }
  }
}

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

public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecks()
        .AddSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);
}

Startup.Configure 内,在应用处理管道中调用运行状况检查中间件:

app.UseHealthChecks("/health");

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

dotnet run --scenario db

备注

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

Entity Framework Core DbContext 探测

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

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

默认情况下:

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

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

public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecks()
        .AddDbContextCheck<AppDbContext>();

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

在示例应用中,UseHealthChecksStartup.Configure 中添加运行状况检查中间件。

app.UseHealthChecks("/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 ) 中进行注册:

public void ConfigureServices(IServiceCollection services)
{
    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");
    });

    // The following workaround permits adding an IHealthCheckPublisher 
    // instance to the service container when one or more other hosted 
    // services have already been added to the app. This workaround
    // won't be required with the release of ASP.NET Core 3.0. For more 
    // information, see: https://github.com/aspnet/Extensions/issues/639.
    services.TryAddEnumerable(
        ServiceDescriptor.Singleton(typeof(IHostedService), 
            typeof(HealthCheckPublisherOptions).Assembly
                .GetType(HealthCheckServiceAssembly)));

    services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();
}

Startup.Configure 内,在应用处理管道中调用运行状况检查中间件。 在示例应用中,在 /health/ready 处为就绪情况检查,并且在 /health/live 处为运行情况检查创建运行状况检查终结点。 就绪情况检查会将运行状况检查筛选到具有 ready 标记的运行状况检查。 运行情况检查通过在 HealthCheckOptions.Predicate 中返回 false 来筛选出 StartupHostedServiceHealthCheck(有关详细信息,请参阅筛选运行状况检查):

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

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

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

dotnet run --scenario liveness

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

此示例还创建了一个运行第一个就绪检查的运行状况检查发布服务器(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 : HealthStatus.Unhealthy;

        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 ) 中:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecks()
        .AddMemoryHealthCheck("memory");
}

Startup.Configure 内,在应用处理管道中调用运行状况检查中间件。 一个 WriteResponse 委托提供给 ResponseWriter 属性,以在执行运行状况检查时输出自定义 JSON 响应:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseHealthChecks("/health", new HealthCheckOptions()
    {
        // This custom writer formats the detailed status as JSON.
        ResponseWriter = WriteResponse
    });
}

WriteResponse 方法将 CompositeHealthCheckResult 格式化为 JSON 对象,并生成运行状况检查响应的 JSON 输出:

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

    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 httpContext.Response.WriteAsync(
        json.ToString(Formatting.Indented));
}

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

dotnet run --scenario writer

备注

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

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

按端口筛选

使用端口调用 UseHealthChecks 会将运行状况检查请求限制到指定端口。 这通常用于在容器环境中公开用于监视服务的端口。

示例应用使用环境变量配置提供程序配置端口。 端口在 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 注册运行状况检查服务。 UseHealthChecks 调用指定管理端口 (ManagementPortStartup.cs ):

public class ManagementPortStartup
{
    public ManagementPortStartup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

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

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseHealthChecks("/health", port: Configuration["ManagementPort"]);

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                "Navigate to " + 
                $"http://localhost:{Configuration["ManagementPort"]}/health " +
                "to see the health status.");
        });
    }
}

备注

可以通过在代码中显式设置 URL 和管理端口,来避免在示例应用中创建 launchSettings.json 文件。 在创建 WebHostBuilder 的 Program.cs 中,添加 UseUrls 调用并提供应用的正常响应终结点和管理端口终结点。 在调用 UseHealthChecks 的 ManagementPortStartup.cs 中,显式指定管理端口。

Program.cs :

return new WebHostBuilder()
    .UseConfiguration(config)
    .UseUrls("http://localhost:5000/;http://localhost:5001/")
    .ConfigureLogging(builder =>
    {
        builder.SetMinimumLevel(LogLevel.Trace);
        builder.AddConfiguration(config);
        builder.AddConsole();
    })
    .UseKestrel()
    .UseStartup(startupType)
    .Build();

ManagementPortStartup.cs :

app.UseHealthChecks("/health", port: 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;
    
    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 秒。

警告

在 ASP.NET Core 2.2 版本中,IHealthCheckPublisher 实现不支持设置 Period,它设置 Delay 的值。 此问题已在 ASP.NET Core 3.0 中得到解决。

在示例应用中,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) 容器中的单一实例服务:

public void ConfigureServices(IServiceCollection services)
{
    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");
    });

    // The following workaround permits adding an IHealthCheckPublisher 
    // instance to the service container when one or more other hosted 
    // services have already been added to the app. This workaround
    // won't be required with the release of ASP.NET Core 3.0. For more 
    // information, see: https://github.com/aspnet/Extensions/issues/639.
    services.TryAddEnumerable(
        ServiceDescriptor.Singleton(typeof(IHostedService), 
            typeof(HealthCheckPublisherOptions).Assembly
                .GetType(HealthCheckServiceAssembly)));

    services.AddSingleton<IHealthCheckPublisher, ReadinessPublisher>();
}

备注

以下解决方法允许在已将一个或多个其他托管服务添加到应用时,向服务容器添加 IHealthCheckPublisher 实例。 ASP.NET Core 3.0 中无需此解决方法。

private const string HealthCheckServiceAssembly =
    "Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckPublisherHostedService";

services.TryAddEnumerable(
    ServiceDescriptor.Singleton(typeof(IHostedService),
        typeof(HealthCheckPublisherOptions).Assembly
            .GetType(HealthCheckServiceAssembly)));

备注

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.UseMvc();

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

上一篇:ASP.NET Core 目录结构

下一篇:使用 IIS 在 Windows 上托管 ASP.NET Core

关注微信小程序
程序员编程王-随时随地学编程

扫描二维码
程序员编程王

扫一扫关注最新编程教程