Panagiotis 的回答非常出色,并为我带来了一个优雅的解决方案,我很想留给下一个遇到此问题的开发人员...
为了在不实现后台服务或任何计时器的情况下实现定期更新,我注册了IHealthCheckPublisher。这样,ASP.NET Core 将自动定期运行注册的健康检查并将其结果发布到相应的实现。
在我的测试中,健康报告默认每 30 秒发布一次。
// add a publisher to cache the latest health report
services.AddSingleton<IHealthCheckPublisher, HealthReportCachePublisher>();
我注册了我的实现 HealthReportCachePublisher,它只是获取已发布的健康报告并将其保存在静态属性中。
我不太喜欢静态属性,但对我来说,这对于这个用例来说似乎已经足够了。
/// <summary>
/// This publisher takes a health report and keeps it as "Latest".
/// Other health checks or endpoints can reuse the latest health report to provide
/// health check APIs without having the checks executed on each request.
/// </summary>
public class HealthReportCachePublisher : IHealthCheckPublisher
{
/// <summary>
/// The latest health report which got published
/// </summary>
public static HealthReport Latest { get; set; }
/// <summary>
/// Publishes a provided report
/// </summary>
/// <param name="report">The result of executing a set of health checks</param>
/// <param name="cancellationToken">A task which will complete when publishing is complete</param>
/// <returns></returns>
public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
{
Latest = report;
return Task.CompletedTask;
}
}
现在真正的魔法发生在这里
正如在每个 Health Checks 示例中看到的那样,我将运行状况检查映射到路由 /health 并使用 UIResponseWriter.WriteHealthCheckUIResponse 返回漂亮的 json 响应。
但我绘制了另一条路线/health/latest。在那里,谓词_ => false 完全阻止执行任何健康检查。但是,我没有返回零健康检查的空结果,而是通过访问静态 HealthReportCachePublisher.Latest 返回之前发布的健康报告。
app.UseEndpoints(endpoints =>
{
// live health data: executes health checks for each request
endpoints.MapHealthChecks("/health", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions()
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
// latest health report: won't execute health checks but return the cached data from the HealthReportCachePublisher
endpoints.MapHealthChecks("/health/latest", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions()
{
Predicate = _ => false, // do not execute any health checks, we just want to return the latest health report
ResponseWriter = (context, _) => UIResponseWriter.WriteHealthCheckUIResponse(context, HealthReportCachePublisher.Latest)
});
});
这样,调用/health 将通过对每个请求执行所有运行状况检查来返回实时运行状况报告。如果有很多事情要检查或要发出网络请求,这可能需要一段时间。
致电/health/latest 将始终返回最新的预先评估的健康报告。这非常快,如果您有一个负载均衡器等待运行状况报告相应地路由传入请求,这可能会很有帮助。
一点补充: 上面的方案使用路由映射取消健康检查的执行,返回最新的健康报告。正如建议的那样,我尝试先构建一个进一步的健康检查,它应该返回最新的缓存健康报告,但这有两个缺点:
- 返回缓存报告本身的新运行状况检查也会出现在结果中(或者必须按名称或标签进行筛选)。
- 没有简单的方法将缓存的健康报告映射到
HealthCheckResult。如果您复制属性和状态代码,这可能会起作用。但是生成的 json 基本上是一个包含内部健康报告的健康报告。这不是你想要的。