asp.net core健康检查需在configureservices中调用addhealthchecks()并启用usehealthchecks()中间件,否则/health返回404;自定义检查器须实现ihealthcheck、全异步、设超时、避免scoped服务注入。

HealthCheck 服务注册必须在 ConfigureService 里完成
ASP.NET Core 的健康检查不是开箱即用的中间件,必须显式注册服务并启用中间件,否则 GET /health 会直接 404。漏掉 AddHealthChecks() 或没调用 UseHealthChecks() 是最常见失败原因。
-
AddHealthChecks()必须在ConfigureServices中调用,且要在AddControllers()或AddEndpointsApiExplorer()之后(否则部分扩展方法不可见) - 若使用 Kestrel + 反向代理(如 Nginx),需确认代理转发了
Host头,否则UseHealthChecks("/health")可能因路径重写失效 - 不要在
Configure里注册健康检查逻辑——那只是配置中间件管道,实际检查器必须提前注入容器
自定义 HealthCheck 类要实现 IHealthCheck 接口并避免阻塞
直接在 CheckHealthAsync 里写 Thread.Sleep(2000) 或调用同步数据库方法(如 SqlConnection.Open()),会导致线程池饥饿、健康端点超时甚至拖垮整个应用。
- 所有 IO 操作必须用异步 API:用
OpenAsync()而非Open(),用HttpClient.GetAsync()而非Get() - 不要在检查器里 new 线程或用
Task.Run(() => { ... })包裹同步逻辑——这绕过了 async/await 上下文,且无法被超时控制 - 建议为每个检查器设置独立超时:用
WithTimeout(TimeSpan.FromSeconds(3))扩展方法(来自Microsoft.Extensions.Diagnostics.HealthChecks)
响应格式和状态码由 UseHealthChecks 自动决定,但可定制
默认情况下,UseHealthChecks("/health") 返回 JSON,且 HTTP 状态码仅取决于整体状态:Healthy → 200,Unhealthy → 503,Degraded → 200(不触发告警)。很多人误以为 Degraded 应该返回 500,其实这是设计使然。
- 若需强制
Degraded返回 503,得自定义HealthCheckPublisher或用中间件拦截响应体后改状态码 - 想返回纯文本(如 Prometheus 兼容格式),需用
UseHealthChecks("/healthz", new HealthCheckOptions { ResponseWriter = WriteTextHealthResponse }) - 注意:Kubernetes 的
livenessProbe默认只认 200,readinessProbe可配failureThreshold,别把Degraded当作“不可用”直接杀 Pod
依赖注入生命周期容易出错:Scoped 服务在 HealthCheck 中不可用
健康检查器本身是 Singleton 生命周期,它内部 resolve 的服务也必须是 Singleton 或 Transient。若注册了一个 Scoped 服务(比如带 EF Core DbContext 的仓储),运行时会抛 InvalidOperationException: Cannot resolve scoped service 'MyContext' from root provider。
- 正确做法:将 DbContext 注册为
Transient(services.AddDbContext<mycontext>(ServiceLifetime.Transient)</mycontext>),或干脆不用 DI,手动 new DbContextOptions 并传入 - 不要在
CheckHealthAsync里直接调用IServiceScopeFactory.CreateScope()—— 这虽能绕过异常,但极易引发内存泄漏(scope 不释放) - 如果检查逻辑确实需要 HttpContext(比如验证当前租户连接字符串),请改用
IHttpContextAccessor,但要注意它本身也是 Scoped,必须通过IServiceProvider在方法内临时解析











