Net Core 2.2 支持对已发布的服务执行“健康检查”。 我想缓存检查的响应。 我发现我可以使用
HealthCheckOptions并为 AllowCachingResponses 属性设置
true
值。
app.UseHealthChecks("/api/services/healthCheck",
new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions()
{
AllowCachingResponses = true
});
但我不明白如何设置缓存时间量。设置相应 HTTP 标头的最佳位置(
Cache-Control
、
Expires
等)以及如何设置?我的服务是由 IIS 发布的。
AllowCachingResponses
选项仅与 HTTP 标头是否由
HealthCheckMiddleware设置有关。通常,中间服务器、代理等可能会缓存 GET 请求的结果,并且这些标头指示服务器每次都应重新获取它们。 但是,如果您的负载均衡器使用这些检查来指示服务是否应该接收更多流量,则无论如何它都可能不会缓存结果。
为了完成您正在寻找的内容,您需要编写额外的逻辑。一种方法是编写一种
HealthCheckCacher
类型,如下所示:
public class HealthCheckCacher : IHealthCheck
{
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1);
private readonly IHealthCheck _healthCheck;
private readonly TimeSpan _timeToLive;
private HealthCheckResult _result;
private DateTime _lastCheck;
public static readonly TimeSpan DefaultTimeToLive = TimeSpan.FromSeconds(30);
/// <summary>
/// Creates a new HealthCheckCacher which will cache the result for the amount of time specified.
/// </summary>
/// <param name="healthCheck">The underlying health check to perform.</param>
/// <param name="timeToLive">The amount of time for which the health check should be cached. Defaults to 30 seconds.</param>
public HealthCheckCacher(IHealthCheck healthCheck, TimeSpan? timeToLive = null)
{
_healthCheck = healthCheck;
_timeToLive = timeToLive ?? DefaultTimeToLive;
}
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
// you could improve thread concurrency by separating the read/write logic but this would require additional thread safety checks.
// will throw OperationCanceledException if the token is canceled while we're waiting.
await _mutex.WaitAsync(cancellationToken);
try
{
// previous check is cached & not yet expired; just return it
if (_lastCheck > DateTime.MinValue && DateTime.Now - _lastCheck < _timeToLive)
return _result;
// check has not been performed or is expired; run it now & cache the result
_result = await _healthCheck.CheckHealthAsync(context, cancellationToken);
_lastCheck = DateTime.Now;
return _result;
}
finally
{
_mutex.Release();
}
}
}
HealthCheckService.CheckHealthAsync()
并存储
HealthReport
结果。然后,我只需创建返回该值的常规 API 端点。更简单,不需要人工包装健康检查。编辑:根据要求,希望有足够的支持代码来演示。
控制器方法:
/// <summary>
/// Returns the last global health check result.
/// </summary>
[AllowAnonymous]
[Route("api/health"), HttpGet]
public HealthReport Health() => HealthJob.LastReport;
还有一些预定的工作。您需要在
BackgroundService
上使用
AddHostedService<HealthJob>()
注册 IServiceCollection
:public class HealthJob : BackgroundService
{
private static readonly TimeSpan CheckInterval = TimeSpan.FromSeconds(10);
// [snip]
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
LastReport = await _healthCheckService.CheckHealthAsync(stoppingToken);
LastExecution = DateTime.Now;
}
catch (TaskCanceledException)
{
// Ignored
}
catch (Exception e)
{
// [snip]
//
// TODO assign LastReport to error values
}
await Task.Delay(CheckInterval, stoppingToken);
}
}
}
public class PeriodHealthCheckPublisher : IHealthCheckPublisher
{
public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2); // Initial delay
options.Period = TimeSpan.FromSeconds(60); // delay between checks
options.Predicate = _ => true;
});
services.AddSingleton<IHealthCheckPublisher, PeriodHealthCheckPublisher>();
services.AddHealthChecks();
此示例将在应用程序启动 2 秒后运行第一次运行状况检查,并每 60 秒运行一次。如果您有一个定期调用运行状况端点的监控系统,这将保护您的 api 免受彻底的攻击,并可以节省资源。