ASP.NET Core 的健康检查开发于2016年秋季开始。当时它是体系结构草案。2016年11月,发布了相关的测试功能,那跟着我来学学相关知识吧。
1、健康检查有啥用?
想象一下,您正创建一个ASP.NET Core应用程序,该应用程序在很大程度上依赖于某些子系统,例如数据库,文件系统,API或类似的东西。
这是一个很常见的情况,几乎每个应用程序都依赖于数据库。
如果与数据库的连接由于各种原因而丢失,则该应用程序肯定会中断。
多年来,好像并没有做什么,可以想象ASP.NET运行状况检查有什么用,但这并不是开发它们的真正原因。
因此,让我们继续以数据库场景为例。
- 如果您在实际连接数据库之前能够检查数据库是否可用,该怎么办?
- 如果您能够告诉应用程序显示数据库不可用的用户友好消息,该怎么办?
- 如果在实际的备用数据库不可用的情况下可以简单地切换到备用数据库该怎么办?
- 如果您的应用程序由于缺少数据库而运行不正常,如果您可以告诉负载均衡器切换到其他后备环境,该怎么办?
您可以使用健康检查做到这些:
检查子系统的运行状况和可用性,提供一个地址以告知其他系统当前应用程序的运行状况,并使其他系统的运行状况检查该地址。
健康检查主要针对微服务环境进行。
松耦合应用程序需要了解它们所依赖的系统的健康状态情况。但是在依赖于某种子系统和基础结构的更单一的应用程序中也很有用。
2、如何启用健康检查?
运行状况检查已经在框架中,您无需添加单独的NuGet包即可使用它。Microsoft.Extensions.Diagnostics.HealthChecks,它应该已经在软件包中。
要启用运行状况检查,您需要将相关服务添加到DI容器中:
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
services.AddControllersWithViews();
}
为了也提供一个端点来告知其他应用程序当前系统的状态,您需要在Startup类的Configure方法内将路由映射到检查:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
这将提供一个URL,你可以在其中检查应用程序的运行状况。
我们的应用程序绝对健康,因为它啥事都没干。
3、编写检查内容
有很多添加运行状况检查的方法。
了解其工作方式的最简单方法和最佳方法是使用lambda方法:
services.AddHealthChecks()
.AddCheck("Foo", () =>
HealthCheckResult.Healthy("Foo is OK!"), tags: new[] { "foo_tag" })
.AddCheck("codeex", () =>
HealthCheckResult.Degraded("codeex is somewhat OK!"), tags: new[] { "bar_tag" })
.AddCheck("webmote", () =>
HealthCheckResult.Unhealthy("webmote is not OK!"), tags: new[] { "foobar_tag" });
这些行添加了三种不同的运行状况检查。它们被起成不同的名称,实际检查是Lambda表达式,该表达式返回特定的HealthCheckResult。结果可能是“健康”,“降级”或“不健康”。
通常,健康检查结果至少具有一个标签,可以按主题或其他内容对它们进行分组。该消息应有意义,以轻松识别实际问题。
这些只是一些演示,但是它们显示了运行状况检查的工作方式。如果我们再次运行该应用程序并调用端点,则会看到不正常状态,因为它始终显示最坏状态,即不正常。
现在,让我们演示一个更有用的健康检查。在Internet上ping所需的资源并检查可用性:
services.AddHealthChecks()
.AddCheck("ping", () =>
{
try
{
using (var ping = new Ping())
{
var reply = ping.Send("asp.net-hacker.rocks");
if (reply.Status != IPStatus.Success)
{
return HealthCheckResult.Unhealthy("Ping is unhealthy");
}
if (reply.RoundtripTime > 100)
{
return HealthCheckResult.Degraded("Ping is degraded");
}
return HealthCheckResult.Healthy("Ping is healthy");
}
}
catch
{
return HealthCheckResult.Unhealthy("Ping is unhealthy");
}
});
当然,这需要ping命令支持,并且有一定的权限才行,我估计大概率是不行的,因为这仍然是个演示例子。
当然这一大坨代码放在Startup类内,看起来很糟糕,是的,你需要换一种方式来进行健康检查。
public class ExampleHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
var healthCheckResultHealthy = true;
if (healthCheckResultHealthy)
{
return Task.FromResult(
HealthCheckResult.Healthy("A healthy result."));
}
return Task.FromResult(
HealthCheckResult.Unhealthy("An unhealthy result."));
}
}
引用此类是下面的语句。
services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>("webmotebased", null, new[] { "label" });
我们还需要指定一个名称和至少一个标签。使用第二个参数,我可以设置默认的失败状态。当然,如果我可以处理运行状况检查中的所有异常,则可以使用null。
4、发送更多健康状况细节
如前所述,我能够提供一个终结点,向依赖于当前应用程序的系统公开我的运行状况。但是默认情况下,它仅使用简单状态的简单字符串进行响应。
还好,能通过传递HealthCheckOptions:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
使用谓词,您可以过滤特定的运行状况检查以执行并获取其状态。
在这里,我想全部执行。在ResponseWriter需要写专项检查的健康信息。
UIResponseWriter 的NuGet:HealthChecks.UI.Client,来自github。
UIResponseWriter项目会将JSON输出写入HTTP响应,其中包括许多详细信息。
如果总体状态为“不正常”,则端点将以HTTP响应状态 503 发送结果,否则为200。如果您只想处理HTTP响应状态,这非常有用。
5、处理应用程序内部的状态
在多数情况下,您不希望仅向应用程序的使用者公开状态。
在某些情况下,您可能需要处理应用的不同状态,方法是显示一条消息,以防应用程序无法正常运行,禁用应用程序中无法正常工作的部分,切换至后备源或其他什么。或者需要以降级状态运行该应用程序。
为此,您可以使用HealthCheckService,您可以在任意位置使用IHealthCheckService注入。
让我们看看它是如何工作的:
public class HomeController : Controller
{
private readonly IHealthCheckService _healthCheckService;
public HomeController(
IHealthCheckService healthCheckService)
{
_healthCheckService = healthCheckService;
}
public async Task<IActionResult> Health()
{
var healthReport = await _healthCheckService.CheckHealthAsync();
return View(healthReport);
}
可以将谓词传递给method CheckHealthAsync()。
使用谓词,可以过滤特定的运行状况检查。代码里,是全部执行它们。
也可以创建了Health.cshtml显示结果
@using Microsoft.Extensions.Diagnostics.HealthChecks;
@model HealthReport
@{
ViewData["Title"] = "Health";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's health.</p>
<p>
<span>@Model.Status</span> - <span>Duration: @Model.TotalDuration.TotalMilliseconds</span>
</p>
<ul>
@foreach (var entry in Model.Entries)
{
<li>
@entry.Value.Status - @entry.Value.Description<br>
Tags: @String.Join(", ", entry.Value.Tags)<br>
Duration: @entry.Value.Duration.TotalMilliseconds
</li>
}
</ul>
6、漂亮的健康状态界面
前面HealthChecks还提供了一个漂亮的UI,以一种易于理解的方式显示结果。
只需在Startup.cs中进行一些配置
services.AddHealthChecksUI();
在Configure()方法内部,需要映射运行状况:
endpoints.MapHealthChecksUI();
这为我们的应用程序添加新的路径来调用UI: /healthchecks-ui
我们还需要将我们的运行状况API注册到UI,通过对appsetings.json进行设置来完成:
{
... ,
"HealthChecksUI": {
"HealthChecks": [
{
"Name": "HTTP-Api",
"Uri": "https://localhost:5001/health"
}
],
"EvaluationTimeOnSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
}
}
这样,您可以根据需要向UI注册尽可能多的运行状况终结点。
该应用程序仅显示您所有微服务的运行状况。
让我们看看用户界面 /healthchecks-ui
哇,这太棒了。这是一个非常好的用户界面,可以显示所有服务的运行状况。
7、小结
健康检查绝对是您应该研究的事情,特别是对于开发微服务的人来说!
无论您正在编写哪种Web应用程序,它都可以帮助您创建更稳定的应用程序。知道其健康状况可以以不会破坏整个应用程序的方式来处理不良状态的降级。
至少从我的角度来看,这非常有用!