.NET健康检查能否基于前置检查结果设置运行时条件?
.NET健康检查的依赖执行控制方案
当然可以实现仅在前序检查健康时才执行后续检查的需求,以下是两种可行的实现方式:
方式一:在后续检查中前置验证依赖检查状态
直接在TokenHealthCheck和DownloadHealthCheck的检查逻辑里,先验证前置检查的结果,只有前置通过才执行自身的检查逻辑。这种方式无需修改健康检查的注册流程,实现简单。
修改TokenHealthCheck
public class TokenHealthCheck : IHealthCheck { private readonly HealthCheckService _healthCheckService; public TokenHealthCheck(HealthCheckService healthCheckService) { _healthCheckService = healthCheckService; } public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { // 先执行并验证证书检查结果 var certCheckResult = await _healthCheckService.CheckHealthAsync( check => check.Name == "Find-certificate", cancellationToken); if (certCheckResult.Status != HealthStatus.Healthy) { // 前置检查失败,直接返回跳过状态(.NET 6+支持) return HealthCheckResult.Skipped("依赖的证书检查未通过,跳过令牌请求检查"); } // 执行原本的令牌请求逻辑 try { // 此处编写获取访问令牌的业务代码 return HealthCheckResult.Healthy("令牌请求成功"); } catch (Exception ex) { return HealthCheckResult.Unhealthy("令牌请求失败", ex); } } }
修改DownloadHealthCheck
public class DownloadHealthCheck : IHealthCheck { private readonly HealthCheckService _healthCheckService; public DownloadHealthCheck(HealthCheckService healthCheckService) { _healthCheckService = healthCheckService; } public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { // 先执行并验证令牌检查结果 var tokenCheckResult = await _healthCheckService.CheckHealthAsync( check => check.Name == "Request-token", cancellationToken); if (tokenCheckResult.Status != HealthStatus.Healthy) { return HealthCheckResult.Skipped("依赖的令牌检查未通过,跳过自定义服务检查"); } // 执行原本的自定义服务访问逻辑 try { // 此处编写调用自定义服务的业务代码 return HealthCheckResult.Healthy("自定义服务访问成功"); } catch (Exception ex) { return HealthCheckResult.Unhealthy("自定义服务访问失败", ex); } } }
方式二:自定义健康检查服务,集中管理依赖链
如果需要更优雅地管理所有检查的依赖关系,避免在每个检查中重复验证逻辑,可以自定义HealthCheckService,控制检查的执行顺序和依赖条件。
实现自定义HealthCheckService
public class CustomHealthCheckService : HealthCheckService { private readonly IEnumerable<IHealthCheck> _healthChecks; private readonly IServiceProvider _serviceProvider; // 定义检查的依赖关系:key为当前检查名称,value为依赖的前置检查名称列表 private readonly Dictionary<string, List<string>> _checkDependencies = new() { { "Request-token", new List<string> { "Find-certificate" } }, { "CustomService", new List<string> { "Request-token" } } }; public CustomHealthCheckService(IEnumerable<IHealthCheck> healthChecks, IServiceProvider serviceProvider) : base(healthChecks, serviceProvider) { _healthChecks = healthChecks; _serviceProvider = serviceProvider; } public override async Task<HealthReport> CheckHealthAsync(Func<HealthCheckRegistration, bool>? predicate = null, CancellationToken cancellationToken = default) { var allRegistrations = base.GetRegistrations(predicate).ToList(); var checkResults = new Dictionary<string, HealthCheckResult>(); var executedChecks = new HashSet<string>(); // 先执行无依赖的检查(数据库、证书检查) foreach (var registration in allRegistrations.Where(r => !_checkDependencies.ContainsKey(r.Name))) { var result = await ExecuteSingleCheckAsync(registration, cancellationToken); checkResults[registration.Name] = result; executedChecks.Add(registration.Name); } // 循环执行有依赖的检查,直到所有检查完成或无法满足依赖 var remainingRegistrations = allRegistrations.Where(r => _checkDependencies.ContainsKey(r.Name)).ToList(); while (remainingRegistrations.Any()) { // 找到所有前置依赖已通过的检查 var executableCheck = remainingRegistrations.FirstOrDefault(reg => _checkDependencies[reg.Name].All(dep => executedChecks.Contains(dep) && checkResults[dep].Status == HealthStatus.Healthy)); if (executableCheck == null) { // 剩余检查无法满足依赖,标记为跳过 foreach (var reg in remainingRegistrations) { checkResults[reg.Name] = HealthCheckResult.Skipped("依赖的前置检查未通过,无法执行"); executedChecks.Add(reg.Name); } break; } // 执行当前检查 var result = await ExecuteSingleCheckAsync(executableCheck, cancellationToken); checkResults[executableCheck.Name] = result; executedChecks.Add(executableCheck.Name); remainingRegistrations.Remove(executableCheck); } // 生成最终健康报告 var overallStatus = checkResults.Values.Max(r => r.Status); var totalDuration = TimeSpan.FromMilliseconds(checkResults.Sum(r => r.Value.Duration.TotalMilliseconds)); return new HealthReport(checkResults, totalDuration, overallStatus); } // 执行单个健康检查的通用方法 private async Task<HealthCheckResult> ExecuteSingleCheckAsync(HealthCheckRegistration registration, CancellationToken cancellationToken) { using var scope = _serviceProvider.CreateScope(); var healthCheck = registration.Factory(scope.ServiceProvider); var context = new HealthCheckContext { Registration = registration }; return await healthCheck.CheckHealthAsync(context, cancellationToken); } }
替换默认的HealthCheckService
在Program.cs中注册自定义服务,替换默认实现:
_builder.Services.AddSingleton<HealthCheckService, CustomHealthCheckService>();
方案选择
- 如果需求简单,推荐方式一,代码改动小,快速实现目标;
- 如果需要管理多个检查的复杂依赖关系,推荐方式二,逻辑集中,可维护性更高。
内容的提问来源于stack exchange,提问作者Vbakke
相关产品推荐
相关产品推荐

