一、为什么健康检查不止是看服务进程
很多后端开发者对健康检查的印象,就是在框架里点几个配置,暴露一个/health端点,返回"OK"就算完事。但如果你的服务是部署在容器、K8s或者微服务环境里,这种检查方式就像给外卖小哥只看你家大门是开着的,但不知道厨房的煤气没开——小哥到了也做不了饭。
举个实际的例子:你做了一个电商订单服务,进程正常跑着,但数据库的连接密码昨天改了,运维忘了同步到这个服务,此时用户点提交订单,还是会收到500错误。内置的健康检查只会说"服务在线",不会管数据库连没连上。这种"假健康"的问题,就是我们要做深度健康检查的原因:它要能帮你看到服务依赖的核心组件(数据库、缓存、消息队列)是不是真的能用。
二、ASP.NET Core内置健康检查的基础用法
2.1 快速启用内置健康检查
ASP.NET Core从3.0开始就自带了健康检查中间件,不用额外装太多插件,基础配置只需要几行代码。我们拿常用的ASP.NET Core 6来举例,完整的示例如下:
var builder = WebApplication.CreateBuilder(args);
// 注册健康检查服务,默认只检查进程是否存活
builder.Services.AddHealthChecks();
var app = builder.Build();
// 暴露健康检查端点,默认返回简单文本
app.MapHealthChecks("/health");
app.Run();
这段代码跑起来后,访问/health会返回"Healthy",但这个检查只是确认服务进程没挂,根本不关心数据库、Redis这些你业务必须的依赖。接下来我们要做的,就是补上这些关键的依赖检查。
三、自定义健康检查:数据库与外部依赖的实现
自定义健康检查的核心,是实现框架提供的IHealthCheck接口,自己写代码去调用依赖的服务,根据返回结果判断健康状态。
3.1 自定义SQL Server数据库健康检查
数据库是绝大多数后端服务的核心依赖,下面是一个完整的SQL Server健康检查示例,注释会说明每个步骤的作用:
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Data.SqlClient;
using System.Threading;
using System.Threading.Tasks;
// 自定义SQL Server健康检查类,实现IHealthCheck接口
public class DatabaseHealthCheck : IHealthCheck
{
// 从配置文件读取数据库连接字符串,避免硬编码
private readonly string _dbConnectionString;
// 构造函数注入配置,ASP.NET Core会自动处理依赖注入
public DatabaseHealthCheck(IConfiguration config)
{
_dbConnectionString = config.GetConnectionString("DefaultSqlDb");
}
// 实现接口的核心方法,返回健康状态
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancel = default)
{
try
{
// 尝试打开数据库连接,模拟业务实际调用的方式
using var conn = new SqlConnection(_dbConnectionString);
await conn.OpenAsync(cancel);
// 连接成功,返回健康状态,附带说明
return HealthCheckResult.Healthy("数据库连接正常");
}
catch (Exception ex)
{
// 连接失败,返回不健康状态,带上具体错误信息方便排查
return HealthCheckResult.Unhealthy($"数据库连接失败:{ex.Message}");
}
}
}
写完这个类后,要把它注册到健康检查服务里,修改Program.cs的代码:
// 注册健康检查,加上自定义的数据库检查
builder.Services.AddHealthChecks()
.AddCheck<DatabaseHealthCheck>("sql_database");
这样健康检查端点就会返回数据库的状态,而不仅仅是进程状态。
3.2 自定义Redis外部依赖健康检查
Redis是常用的外部缓存,很多业务依赖它的读写,同样需要做健康检查。示例代码如下:
using Microsoft.Extensions.Diagnostics.HealthChecks;
using StackExchange.Redis;
using System;
using System.Threading;
using System.Threading.Tasks;
// 自定义Redis健康检查类
public class RedisHealthCheck : IHealthCheck
{
private readonly IConnectionMultiplexer _redisConn;
// 构造函数注入Redis连接实例,需要先注册这个服务
public RedisHealthCheck(IConnectionMultiplexer redisConn)
{
_redisConn = redisConn;
}
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancel = default)
{
try
{
// 执行Redis Ping命令,验证连接有效性
var db = _redisConn.GetDatabase();
var pingTime = await db.PingAsync(cancel);
// 根据Ping结果判断状态,正常Ping会返回时间,异常会超时
return pingTime < TimeSpan.FromSeconds(1)
? HealthCheckResult.Healthy($"Redis连接正常,延迟:{pingTime.TotalMilliseconds}ms")
: HealthCheckResult.Unhealthy("Redis连接延迟过高");
}
catch (Exception ex)
{
return HealthCheckResult.Unhealthy($"Redis检查失败:{ex.Message}");
}
}
}
同样要注册Redis连接和健康检查,修改Program.cs:
// 先注册Redis连接,从配置读取连接字符串
builder.Services.AddSingleton<IConnectionMultiplexer>(sp =>
ConnectionMultiplexer.Connect(builder.Configuration["Redis:ConnectionString"]));
// 注册健康检查,加上Redis检查
builder.Services.AddHealthChecks()
.AddCheck<RedisHealthCheck>("redis_cache");
此时访问/health,就会同时返回数据库和Redis的健康状态。
四、深度健康检查的实际应用场景
这种自定义的健康检查,不是为了写代码而写,而是有明确的使用场景:
- 容器/ Kubernetes部署:K8s的
readinessProbe会用健康检查来判断服务是否可以接收流量,如果健康检查失败,K8s会把服务从负载均衡里移除,避免把请求发到有问题的服务。 - 监控系统集成:比如和Prometheus、Grafana配合,把健康检查的结果做成仪表盘,当数据库或Redis异常时,会触发报警,开发能第一时间收到通知,不用等用户反馈。
- 服务自愈:结合K8s的
livenessProbe,如果健康检查持续失败,K8s会自动重启该服务,减少人工干预的成本。
五、技术优缺点分析
5.1 优点
- 更精准的故障检测:能覆盖业务核心依赖,避免假健康问题,提升服务可用性。
- 兼容性好:和ASP.NET Core的依赖注入、配置系统无缝集成,不用额外引入复杂的第三方工具。
- 扩展性强:除了数据库、Redis,还可以检查消息队列、外部API、存储服务等任何你需要的依赖。
5.2 缺点
- 维护成本增加:每个自定义检查需要写代码,多服务共用时要注意复用,避免重复造轮子。
- 性能开销:如果检查的依赖太多,每次健康检查会增加服务的响应时间,所以只需要检查核心依赖即可。
- 配置复杂:不同环境(开发、测试、生产)的连接字符串不同,需要做好配置管理,避免硬编码。
六、注意事项
- 控制检查数量:只检查对业务不可缺少的依赖,比如日志服务、统计服务这种非核心依赖,没必要每次健康检查都去连。
- 设置合理的超时时间:比如数据库连接超时设3-5秒,Redis Ping超时设1秒,避免健康检查本身因为等待太久,导致服务线程阻塞。
- 隔离健康检查的资源:比如检查数据库时,用单独的连接字符串,不要占用业务的数据库连接池,避免影响正常业务。
- 测试环境和生产环境区分:生产环境的健康检查要更严格,比如不允许跳过检查,而开发环境可以设长一点的超时,方便调试。
七、总结
健康检查不是一个可有可无的功能,而是保障后端服务稳定的关键一环。从只看进程的内置检查,到自定义的数据库、外部依赖检查,核心是要让系统真的知道自己能不能干活。本文的示例都是基于ASP.NET Core的,不管你是新手还是有一定经验的开发者,都能跟着步骤实现自己的深度健康检查,解决服务假健康的问题,提升用户体验。
Comments