ASP.NET Core健康检查需通过内置HealthCheckService和中间件实现,而非手写接口;必须封装IHealthCheck、注册服务、传递cancellationToken、启用JSON响应并遵守超时约束。

ASP.NET Core 的健康检查不是靠手写接口实现的,而是用内置的 HealthCheckService 和中间件自动注册、调度和响应——直接写一个 GET /health 返回 JSON 是错的,会漏掉超时控制、依赖隔离、状态聚合等关键能力。
怎么注册基础健康检查(如数据库连通性)
不能只调用 SqlConnection.Open() 然后返回 bool;必须封装成实现 IHealthCheck 的类,再通过 DI 注册。框架才能统一执行、加超时、记录耗时。
- 新建类继承
IHealthCheck,实现CheckHealthAsync方法,在里面做真实探测(比如 EF Core 的Database.CanConnectAsync()) - 在
Program.cs中调用services.AddHealthChecks().AddCheck<MyDbHealthCheck>("db") - 不要在
CheckHealthAsync里 throw 异常——要用HealthCheckResult.Unhealthy()显式返回失败,否则会触发全局异常处理,掩盖真实健康状态 - 如果检查逻辑本身可能阻塞(如旧版 ADO.NET 同步调用),必须包装成
Task.Run或改用异步 API,否则拖垮整个健康检查线程池
如何让 HTTP 健康端点返回结构化 JSON(而非纯文本)
默认 MapHealthChecks("/health") 返回的是纯文本(如 "Healthy"),K8s 或 Consul 等工具无法解析——必须显式启用 JSON 响应格式。
- 添加 NuGet 包:
Microsoft.AspNetCore.Diagnostics.HealthChecks(.NET 6+ 已内置,但需确认版本) - 注册时加上
.AddJsonResponseWriter():app.MapHealthChecks("/health", new HealthCheckOptions { ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); - 更推荐自定义 writer,避免引入 UI 包(它带 Razor 依赖):
options.ResponseWriter = async (ctx, report) => { ctx.Response.ContentType = "application/json"; await JsonSerializer.SerializeAsync(ctx.Response.Body, report, new JsonSerializerOptions { WriteIndented = true }); }; - 注意:K8s 的
livenessProbe默认只认 HTTP 200,不关心内容。但readinessProbe若配置了failureThreshold,JSON 格式错误会导致反复重启
为什么自定义检查里 await Task.Delay(1000) 会导致健康检查超时失败
不是代码写错了,是 ASP.NET Core 健康检查默认超时是 30 秒,但所有检查共享同一个 CancellationToken——某个检查用了 Task.Delay 却没传 token,就变成“假等待”,实际没响应取消信号,最终拖垮整组检查。
- 所有异步操作必须接收并传递
cancellationToken参数:await Task.Delay(1000, cancellationToken) - 外部 HTTP 调用(如检查第三方服务)必须设
HttpClient.Timeout,且该 timeout 必须小于健康检查总 timeout(建议 ≤10s) - EF Core 的
CanConnectAsync()内部已支持 cancellation,但若你手动写new SqlConnection(...).OpenAsync(cancellationToken),也得确保 connection string 含Connection Timeout=5 - 本地文件检查(如监控 config.json 修改时间)别用
File.GetLastWriteTimeUtc()+DateTime.Now比较——NTFS 时间精度可能达 100ns,导致误判“刚更新过”,应加 1s 容差
健康检查真正的难点不在写逻辑,而在理解它是一套“受控的并发探测系统”:每个检查是独立 task,有统一超时、可取消、结果被聚合。漏传 cancellationToken、混用同步/异步 IO、或把健康端点当普通 API 来写,都会让探测失真——尤其在容器编排场景下,一个看似健康的实例可能已被调度器反复驱逐。


















