系列导航及源代码

需求

应用健康检查在容器部署的微服务场景下非常常见,相比而言单体非容器部署的应用就不太关心这个特性,为了后续的内容我们在本文中简单介绍一下如何实现应用程序的健康检查功能。

目标

实现TodoList的健康检查功能。

原理与思路

.NET框架从.NET Core 2.2版本开始引入了相关的功能,同时AspNetCore.Diagnostics.HealthChecks包提供了更为丰富的健康检查功能,包括数据库,消息总线,Redis和ElasticSearch的健康检查。

健康检查探针(probe)分为三种:

  • readiness probes:应用程序就绪探针,判断当前应用程序是否已经启动。
  • liveness probes: 应用程序存活探针,判断当前应用程序是否出于活动状态。
  • startup probes:应用程序功能探针,一般我们用hc来表示其端口,可以用来判断应用程序内部功能是否正常(比如判断数据库连接是否正常,判断Redis连接是否正常之类)。

在本文中我们不过多地发散,通过创建一个自定义的健康检查对象来实现。

实现

自定义健康检查,实现IHealthCheck接口

Microsoft.Extensions.Diagnostics.HealthChecks

using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace TodoList.Application.Common;

public class ApplicationHealthCheck : IHealthCheck
{
    private static readonly Random _rnd = new ();

    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        var result = _rnd.Next(5) == 0
            ? HealthCheckResult.Healthy()
            : HealthCheckResult.Unhealthy("Failed random");

        return Task.FromResult(result);
    }
}

添加服务和中间件

首先在Application/DependencyInjection中添加健康检查服务:

  • DependencyInjection.cs
services.AddHealthChecks().AddCheck<ApplicationHealthCheck>("Random Health Check");

然后修改Program中间件配置如下:

  • Program.cs
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseHttpLogging();

app.MapDefaultControllerRoute();
app.MapHealthChecks("/liveness");
app.MapHealthChecks("/ready");
app.MapHealthChecks("/hc");

验证

启动Api项目,执行健康检查的三个GET接口,截图我只贴其中一个,因为我们在实现健康检查的时候,采用了随机数返回Healthy或者Unhealthy的方式,所以多请求几次,应该能看到下面的响应:

一点扩展

关于Smart probes和Dumb probes

  • 所谓Smart probes最典型的目的就是为了验证应用程序是否能正确工作,像一开始所说的,它可以用来探测外部依赖如数据库、消息队列、甚至是其他API的连接是否正常,程序在这些地方是否可以按预期工作。
  • 相对而言,Dumb probes典型的应用目的是用来判断应用程序没有崩溃,它并不检查功能及依赖上的正确性,通常表现为HTTP的直接响应。

推荐的使用方式是:

  • 对于liveness检查,使用dumb probes就可以了,因为只要能够响应HTTP请求,就可以证明应用程序没有崩溃。
  • 对于startup或者称作真正的“健康”检查hc,使用smart probes,因为这时候不仅要保证程序没有崩溃,我们还需要某种程度的功能及依赖的健康检查,以证明程序能够按预期工作。
  • 对于readiness检查,需要根据实际的需求来看,如果我们没有定义特殊的应用程序“就绪”标准,使用dumb probes就可以;反之如果我们需要进行某种程度的逻辑检查来定义“就绪”,那么就需要使用smart probes

设置Dumb健康检查接口

修改中间件配置:

  • Program.cs
app.MapHealthChecks("/liveness", new HealthCheckOptions { Predicate = r => r.Name.Contains("self") });
app.MapHealthChecks("/ready", new HealthCheckOptions { Predicate = _ => false });

这时候我们再去请求健康检查,这两个接口已经固定返回Healthy结果了,hc接口依然按照我们之前设置的进行随机返回。

总结

本文我们简单地实现了健康检查接口,目前还不太能看得出来作用,但是当我们进行容器化部署,或是进行k8s部署时,健康检查探针的作用就比较明显了。关于健康检查,更多用法请参考官方文档:Health checks in ASP.NET Core

参考资料

  1. AspNetCore.Diagnostics.HealthChecks
  2. Adding health checks with Liveness, Readiness, and Startup probes
  3. Health checks in ASP.NET Core