ASP.NET Core健康检查需服务注册、端点暴露、检查逻辑三者协同;漏任一环则/health 404或恒返Healthy,必须调用AddHealthChecks()注册服务、MapHealthChecks()暴露端点,并确保自定义检查异步无阻塞。

ASP.NET Core 的 HealthCheck 不是“加个中间件就能用”的开关,它依赖服务注册、端点暴露和检查逻辑三者协同;漏掉任一环,/health 就会 404 或始终返回 Healthy。
注册 HealthCheck 服务时必须调用 AddHealthChecks()
这是最常被跳过的一步——只配置了端点,却没注册服务。ASP.NET Core 的健康检查是基于 DI 容器构建的,不注册就无法解析 IHealthCheckService。
-
AddHealthChecks()必须在Program.cs的builder.Services阶段调用,哪怕只是空注册:builder.Services.AddHealthChecks(); - 若要添加自定义检查(比如检查数据库连接),需链式调用
AddCheck或() AddCheck("name", new MyDbHealthCheck()) - 别把
AddHealthChecks()和MapHealthChecks()搞混:前者注册服务,后者暴露 HTTP 端点,二者缺一不可
暴露健康检查端点要用 MapHealthChecks() 而非普通路由
MapHealthChecks() 不是语法糖,它内部自动挂载了 HealthCheckMiddleware 并处理序列化、状态码、缓存头等细节;手写 MapGet("/health", ...) 会丢失这些能力,且默认返回 200 OK,无法体现真实状态。
- 推荐写法:
app.MapHealthChecks("/health");(返回 JSON,默认application/json) - 如需兼容旧系统或简化响应,可加参数:
app.MapHealthChecks("/health", new HealthCheckOptions { ResponseWriter = WritePlainHealthyResponse }); - 注意路径冲突:如果同时用了
MapControllers()和MapHealthChecks("/health"),确保/health不被控制器路由捕获(比如没写[Route("health")]的控制器)
自定义 IHealthCheck 实现必须异步且不能阻塞
所有健康检查都会被并发执行,若实现中用了 .Result、.Wait() 或同步 I/O(如 File.Exists()),会拖慢整个健康端点响应,甚至引发线程池饥饿。
- 接口方法签名强制为
Task,必须用CheckHealthAsync(...) await处理耗时操作 - 数据库检查不要写
context.Database.CanConnect()(同步),改用await context.Database.CanConnectAsync() - HTTP 依赖检查请用
HttpClient+await client.GetAsync(...),并设置CancellationToken防止超时卡死 - 避免在检查里做重试逻辑——健康检查应快(理想
HealthCheckPublisher 适合后台周期性上报,但别和 HTTP 端点混用
如果你看到文档提到 AddHealthCheckPublisher,它和 MapHealthChecks() 是两条路:前者用于向 Prometheus、EventLog 或消息队列推送状态,后者供人工或探针 HTTP 调用。两者注册方式、触发时机、输出目标完全不同。
- 启用发布器需额外调用
services.AddHealthChecks().AddPublisher,且要配置后台主机服务(() HostBuilder.ConfigureServices(...).AddHostedService)() - 发布器默认每 30 秒执行一次,频率由
HealthCheckPublisherOptions.Period控制,与 HTTP 端点的实时性无关 - 别试图在发布器里调用
IHealthCheckService.CheckHealthAsync()再转推——这会重复执行检查逻辑,增加负载;应复用同一套检查实例
最容易被忽略的是:健康检查的失败判定只看 HealthCheckResult.Unhealthy() 或异常,但很多开发者在自定义检查里忘了 try/catch,导致未处理异常直接让整个端点 500,而不是返回 Unhealthy 带错误信息。写检查逻辑时,宁可显式捕获异常并封装进 HealthCheckResult.Unhealthy("msg", ex),也不要让它逃逸。











