如何通过功能标志动态启停AspNetCore.Diagnostics.HealthChecksUI的轮询?
动态控制AspNetCore.Diagnostics.HealthChecksUI轮询启停的方案
不需要反射访问internal的HealthCheckReportCollector,有两种简便的实现方式,核心思路都是基于依赖注入替换或包装现有服务,结合功能标志控制逻辑:
方案一:替换轮询HostedService
原轮询逻辑由HealthCheckCollectorHostedService触发,我们可以直接替换这个HostedService,在自定义实现中根据功能标志决定是否执行健康检查收集:
- 实现自定义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(); } }
- 在Program.cs中替换原有HostedService注册:
// 移除原HealthCheckCollectorHostedService的注册 builder.Services.RemoveAll<IHostedService>(service => service.ImplementationType == typeof(HealthCheckCollectorHostedService)); // 添加自定义HostedService builder.Services.AddSingleton<IHostedService, FeatureFlagHealthCheckCollectorHostedService>();
方案二:包装IHealthCheckReportCollector
通过装饰器模式包装公开的IHealthCheckReportCollector接口,在Collect方法中加入功能标志判断:
- 实现装饰器类:
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); } } }
- 在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
相关产品推荐
相关产品推荐

