Panagiotis的答案非常棒,带我找到了一种优雅的解决方案,我很想留下来给下一个遇到这个问题的开发人员......
为了实现周期性更新而不需要实现后台服务或任何计时器,我注册了一个。通过这个,ASP.NET Core会定期运行已注册的健康检查并将它们的结果发布到相应的实现中。
在我的测试中,健康报告默认每30秒发布一次。
// add a publisher to cache the latest health report
services.AddSingleton<IHealthCheckPublisher, HealthReportCachePublisher>();
我注册了我的实现 HealthReportCachePublisher
,它不做任何其他事情,只是将已发布的健康报告保存在静态属性中。
我并不是很喜欢静态属性,但在这种情况下似乎足够适用。
public class HealthReportCachePublisher : IHealthCheckPublisher
{
public static HealthReport Latest { get; set; }
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 =>
{
endpoints.MapHealthChecks("/health", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions()
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecks("/health/latest", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions()
{
Predicate = _ => false,
ResponseWriter = (context, _) => UIResponseWriter.WriteHealthCheckUIResponse(context, HealthReportCachePublisher.Latest)
});
});
这种方式调用 /health
将会在每个请求上执行所有健康检查并返回实时的健康报告。如果有很多要检查或需要进行网络请求,这可能需要一些时间。
调用 /health/latest
将始终返回最新的预先评估的健康报告。这非常快速,可以帮助负载均衡器根据健康报告来路由传入的请求。
稍作补充: 上面的解决方案使用路由映射取消健康检查的执行,并返回最新的健康报告。如建议所示,我试图首先构建一个进一步的健康检查,该检查应返回最新的缓存健康报告,但这有两个缺点:
- 新的健康检查也将出现在结果中(或必须通过名称或标记进行筛选)。
- 没有简单的方法将缓存的健康报告映射到
HealthCheckResult
。如果复制属性和状态代码,则可能有效。但是,生成的 JSON 基本上是一个包含内部健康报告的健康报告。那不是你想要的结果。