You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过功能标志动态启停AspNetCore.Diagnostics.HealthChecksUI的轮询?

动态控制AspNetCore.Diagnostics.HealthChecksUI轮询启停的方案

不需要反射访问internal的HealthCheckReportCollector,有两种简便的实现方式,核心思路都是基于依赖注入替换或包装现有服务,结合功能标志控制逻辑:

方案一:替换轮询HostedService

原轮询逻辑由HealthCheckCollectorHostedService触发,我们可以直接替换这个HostedService,在自定义实现中根据功能标志决定是否执行健康检查收集:

  1. 实现自定义HostedService:
public class FeatureFlagHealthCheckCollectorHostedService : IHostedService, IDisposable
{
    private readonly IHealthCheckReportCollector _reportCollector;
    private readonly IOptions<HealthChecksUIOptions> _uiOptions;
    private readonly IFeatureManager _featureManager;
    private Task _pollingTask;
    private CancellationTokenSource _cts;

    public FeatureFlagHealthCheckCollectorHostedService(
        IHealthCheckReportCollector reportCollector,
        IOptions<HealthChecksUIOptions> uiOptions,
        IFeatureManager featureManager)
    {
        _reportCollector = reportCollector;
        _uiOptions = uiOptions;
        _featureManager = featureManager;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        _pollingTask = RunPollingLoop(_cts.Token);
        return Task.CompletedTask;
    }

    private async Task RunPollingLoop(CancellationToken stoppingToken)
    {
        var pollingInterval = _uiOptions.Value.PollingInterval ?? TimeSpan.FromSeconds(30);

        while (!stoppingToken.IsCancellationRequested)
        {
            // 检查功能标志,替换为你的标志名称
            var isPollingEnabled = await _featureManager.IsEnabledAsync("HealthCheckPollingEnabled");
            
            if (isPollingEnabled)
            {
                await _reportCollector.Collect(stoppingToken);
            }

            await Task.Delay(pollingInterval, stoppingToken);
        }
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        _cts?.Cancel();
        if (_pollingTask != null)
        {
            await Task.WhenAny(_pollingTask, Task.Delay(Timeout.Infinite, cancellationToken));
        }
    }

    public void Dispose()
    {
        _cts?.Dispose();
    }
}
  1. 在Program.cs中替换原有HostedService注册:
// 移除原HealthCheckCollectorHostedService的注册
builder.Services.RemoveAll<IHostedService>(service => 
    service.ImplementationType == typeof(HealthCheckCollectorHostedService));

// 添加自定义HostedService
builder.Services.AddSingleton<IHostedService, FeatureFlagHealthCheckCollectorHostedService>();

方案二:包装IHealthCheckReportCollector

通过装饰器模式包装公开的IHealthCheckReportCollector接口,在Collect方法中加入功能标志判断:

  1. 实现装饰器类:
public class FeatureFlagControlledReportCollector : IHealthCheckReportCollector
{
    private readonly IHealthCheckReportCollector _originalCollector;
    private readonly IFeatureManager _featureManager;

    public FeatureFlagControlledReportCollector(
        IHealthCheckReportCollector originalCollector,
        IFeatureManager featureManager)
    {
        _originalCollector = originalCollector;
        _featureManager = featureManager;
    }

    public async Task Collect(CancellationToken cancellationToken = default)
    {
        var isPollingEnabled = await _featureManager.IsEnabledAsync("HealthCheckPollingEnabled");
        if (isPollingEnabled)
        {
            await _originalCollector.Collect(cancellationToken);
        }
    }
}
  1. 在Program.cs中替换原有Collector注册:
// 获取原IHealthCheckReportCollector的注册项
var originalCollectorDescriptor = builder.Services.FirstOrDefault(sd => 
    sd.ServiceType == typeof(IHealthCheckReportCollector));

if (originalCollectorDescriptor != null)
{
    builder.Services.Remove(originalCollectorDescriptor);
    
    // 注册装饰器,注入原Collector实例
    builder.Services.AddSingleton<IHealthCheckReportCollector>(sp =>
    {
        var originalCollector = (IHealthCheckReportCollector)ActivatorUtilities.CreateInstance(
            sp, 
            originalCollectorDescriptor.ImplementationType, 
            originalCollectorDescriptor.ImplementationInstance ?? sp.GetService(originalCollectorDescriptor.ServiceType));
        
        var featureManager = sp.GetRequiredService<IFeatureManager>();
        return new FeatureFlagControlledReportCollector(originalCollector, featureManager);
    });
}

方案对比

  • 方案一更贴近轮询的触发逻辑,禁用时不会执行Collect方法,同时保持原有的轮询间隔节奏。
  • 方案二更聚焦于收集逻辑的控制,原HostedService的循环仍在运行,但实际收集操作会被跳过。

两种方案都无需反射访问internal类型,完全依赖公开的接口和依赖注入机制实现。

内容的提问来源于stack exchange,提问作者handles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:57:35