.NET Core 3.0之深入源码理解HealthCheck(一)
写在前面
我们的系统可能因为正在部署、服务异常终止或者其他问题导致系统处于非健康状态,这个时候我们需要知道系统的健康状况,而健康检查可以帮助我们快速确定系统是否处于正常状态。一般情况下,我们会提供公开的HTTP接口,用于专门化健康检查。
NET Core提供的健康检查库包括Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions和Microsoft.Extensions.Diagnostics.HealthChecks。这两个库共同为我们提供了最基础的健康检查的解决方案,后面扩展的组件主要有下面几个,本文不作其他说明。
AspNetCore.HealthChecks.System
AspNetCore.HealthChecks.Network
AspNetCore.HealthChecks.SqlServer
AspNetCore.HealthChecks.MongoDb
AspNetCore.HealthChecks.Npgsql
AspNetCore.HealthChecks.Redis
AspNetCore.HealthChecks.AzureStorage
AspNetCore.HealthChecks.AzureServiceBus
AspNetCore.HealthChecks.MySql
AspNetCore.HealthChecks.DocumentDb
AspNetCore.HealthChecks.SqLite
AspNetCore.HealthChecks.Kafka
AspNetCore.HealthChecks.RabbitMQ
AspNetCore.HealthChecks.IdSvr
AspNetCore.HealthChecks.DynamoDB
AspNetCore.HealthChecks.Oracle
AspNetCore.HealthChecks.Uris
源码探究
Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions是.NET Core健康检查的抽象基础,从中我们可以看出这个库的设计意图。它提供了一个统一的接口IHealthCheck,用于检查应用程序中各个被监控组件的状态,包括后台服务、数据库等。这个接口只有一个方法CheckHealthAsync,
该方法有一个参数是HealthCheckContext,它表示当前健康检查执行时所关联的上下文对象,它的返回值HealthCheckResult表示当前健康检查结束后所产生的被监控组件的运行状态。
源码如下所示:
1: public interface IHealthCheck
2: {
3: Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default);
4: }
HealthCheckRegistration
HealthCheckContext里面只有一个成员就是HealthCheckRegistration实例。
而HealthCheckRegistration是一个相当重要的对象,它体现了健康检查需要关注和注意的地方,其内部涉及到五个属性,分别用于:
- 标识健康检查名称
- 创建IHealthCheck实例
- 健康检查的超时时间(防止我们因为健康检查而过多占用资源)
- 失败状态标识
- 一个标签集合(可用于健康检查过滤)
这五个属性的相关源码如下:
1: public Func<IServiceProvider, IHealthCheck> Factory
2: {
3: get => _factory;
4: set
5: {
6: if (value == null)
7: {
8: throw new ArgumentNullException(nameof(value));
9: }
10:
11: _factory = value;
12: }
13: }
14:
15: public HealthStatus FailureStatus { get; set; }
16:
17: public TimeSpan Timeout
18: {
19: get => _timeout;
20: set
21: {
22: if (value <= TimeSpan.Zero && value != System.Threading.Timeout.InfiniteTimeSpan)
23: {
24: throw new ArgumentOutOfRangeException(nameof(value));
25: }
26:
27: _timeout = value;
28: }
29: }
30:
31: public string Name
32: {
33: get => _name;
34: set
35: {
36: if (value == null)
37: {
38: throw new ArgumentNullException(nameof(value));
39: }
40:
41: _name = value;
42: }
43: }
44:
45: public ISet<string> Tags { get; }
HealthCheckResult
HealthCheckResult是一个结构体,可以看出这里更多的是基于承担数据存储和性能问题的考量。
HealthCheckResult用于表示健康检查的相关结果信息,同样的,通过该类,我们知道了健康检查需要关注的几个点:
- 组件的当前状态
- 异常信息
- 友好的描述信息(不管是异常还是正常)
- 额外可描述当前组件的键值对,这是一个开放式的属性,方面我们记录更多信息
该类含有四个公共属性,和三个方法,相关源码如下:
1: public struct HealthCheckResult
2: {
3: private static readonly IReadOnlyDictionary<string, object> _emptyReadOnlyDictionary = new Dictionary<string, object>();
4:
5: public HealthCheckResult(HealthStatus status, string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null)
6: {
7: Status = status;
8: Description = description;
9: Exception = exception;
10: Data = data ?? _emptyReadOnlyDictionary;
11: }
12:
13: public IReadOnlyDictionary<string, object> Data { get; }
14:
15: public string Description { get; }
16:
17: public Exception Exception { get; }
18:
19: public HealthStatus Status { get; }
20:
21: public static HealthCheckResult Healthy(string description = null, IReadOnlyDictionary<string, object> data = null)
22: {
23: return new HealthCheckResult(status: HealthStatus.Healthy, description, exception: null, data);
24: }
25:
26: public static HealthCheckResult Degraded(string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null)
27: {
28: return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: exception, data);
29: }
30:
31: public static HealthCheckResult Unhealthy(string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null)
32: {
33: return new HealthCheckResult(status: HealthStatus.Unhealthy, description, exception, data);
34: }
35: }
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
-->
可以看出这个三个方法都是基于HealthStatus这个枚举而创建不同状态的HealthCheckResult实例,这个枚举表达了健康检查需要关注的几种状态,健康、异常以及降级。
HealthStatus的源码如下:
1: public enum HealthStatus
2: {
3: Unhealthy = 0,
4:
5: Degraded = 1,
6:
7: Healthy = 2,
8: }
IHealthCheckPublisher
健康检查功能本质上是一种轮询功能,需要定期执行,.NET Core 抽象定期执行的接口,即IHealthCheckPublisher,我们可以通过实现这个接口,并与我们自定义的定时功能相结合。
同时,作为一次健康检查,我们还需要关注相关的健康检查报告,那么我们需要关注那些点呢?
- 额外可描述当前组件的键值对,这是一个开放式的属性,方面我们记录更多信息
- 友好的描述信息(不管是异常还是正常)
- 组件的当前状态
- 异常信息
- 当前这次检查所耗费的时间
- 相关的标签信息
HealthReportEntry表示单个健康检查报告,HealthReport表示一组健康检查报告。HealthReport内部维护了一个HealthReportEntry的字典数据,HealthReport源码如下所示:
1: public sealed class HealthReport
2: {
3: public HealthReport(IReadOnlyDictionary<string, HealthReportEntry> entries, TimeSpan totalDuration)
4: {
5: Entries = entries;
6: Status = CalculateAggregateStatus(entries.Values);
7: TotalDuration = totalDuration;
8: }
9:
10: public IReadOnlyDictionary<string, HealthReportEntry> Entries { get; }
11:
12: public HealthStatus Status { get; }
13:
14: public TimeSpan TotalDuration { get; }
15:
16: private HealthStatus CalculateAggregateStatus(IEnumerable<HealthReportEntry> entries)
17: {
18: var currentValue = HealthStatus.Healthy;
19: foreach (var entry in entries)
20: {
21: if (currentValue > entry.Status)
22: {
23: currentValue = entry.Status;
24: }
25:
26: if (currentValue == HealthStatus.Unhealthy)
27: {
28: // Game over, man! Game over!
29: // (We hit the worst possible status, so there's no need to keep iterating)
30: return currentValue;
31: }
32: }
33:
34: return currentValue;
35: }
36: }
总结
通过以上内容,我们知道了,一个完整的健康检查需要关注健康检查上下文、健康状态的维护、健康检查结果、健康检查报告,同时,为了更好的维护健康检查,我们可以将健康检查发布抽象出来,并与外部的定时器相结合,共同守护健康检查程序。
.NET Core 3.0之深入源码理解HealthCheck(一)的更多相关文章
- .NET Core 3.0之深入源码理解Startup的注册及运行
原文:.NET Core 3.0之深入源码理解Startup的注册及运行 写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程 ...
- .NET Core 3.0之深入源码理解Configuration(一)
Configuration总体介绍 微软在.NET Core里设计出了全新的配置体系,并以非常灵活.可扩展的方式实现.从其源码来看,其运行机制大致是,根据其Source,创建一个Builder实例,并 ...
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(一)
写在前面 ASP.NET Core 的 Web 服务器默认采用Kestrel,这是一个基于libuv(一个跨平台的基于Node.js异步I/O库)的跨平台.轻量级的Web服务器. 在开始之前,先回 ...
- .NET Core 3.0之深入源码理解Kestrel的集成与应用(二)
前言 前一篇文章主要介绍了.NET Core继承Kestrel的目的.运行方式以及相关的使用,接下来将进一步从源码角度探讨.NET Core 3.0中关于Kestrel的其他内容,该部分内容,我们 ...
- .NET Core 3.0之深入源码理解HttpClientFactory(二)
写在前面 上一篇文章讨论了通过在ConfigureServices中调用services.AddHttpClient()方法,并基于此进一步探讨了DefaultHttpClientFactory是 ...
- .NET Core 3.0之深入源码理解Host(二)
写在前面 停了近一个月的技术博客,随着正式脱离996的魔窟,接下来也正式恢复了.本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点,主要讨论Long Run Pro ...
- .NET Core 3.0之深入源码理解ObjectPool(一)
写在前面 对象池是一种比较常用的提高系统性能的软件设计模式,它维护了一系列相关对象列表的容器对象,这些对象可以随时重复使用,对象池节省了频繁创建对象的开销. 它使用取用/归还的操作模式,并重复执行这些 ...
- .NET Core 3.0之深入源码理解Host(一)
写在前面 ASP .NET Core中的通用主机构建器是在v2.1中引入的,应用在启动时构建主机,主机作为一个对象用于封装应用资源以及应用程序启动和生存期管理.其主要功能包括配置初始化(包括加载配置以 ...
- .NET Core 3.0之深入源码理解Configuration(三)
写在前面 上一篇文章讨论了文件型配置的基本内容,本篇内容讨论JSON型配置的实现方式,理解了这一种配置类型的实现方式,那么其他类型的配置实现方式基本可以触类旁通.看过了上一篇文章的朋友,应该看得出 ...
随机推荐
- Django学习day8——admin后台管理和语言适应
Django最大的优点之一,就是体贴的为你提供了一个基于项目model创建的一个后台管理站点admin.这个界面只给站点管理员使用,并不对大众开放. 1. 创建管理员用户 (django) E:\Dj ...
- [考试反思]1024csp-s模拟测试85:以为
愈发垃圾. T1基本全场切(除了RP<-inf的zkt和把人擦) 然后T2想了半天逐渐趋近于正解,但是因为数据有问题锅了25分,没什么好说的.T3连题意转化都没有完成.括号匹配转为+1/-1做法 ...
- 「Usaco2008 Jan」人工湖O(∩_∩)O 纯属的模拟+栈
题目描述 夏日那让人喘不过气的酷热将奶牛们的烦躁情绪推到了最高点.最终,约翰决定建一个人工湖供奶牛消暑之用. 为了使湖看起来更加真实,约翰决定将湖的横截面建成N(1≤N≤105)个连续的平台高低错落的 ...
- 腾讯正式开源图计算框架Plato,十亿级节点图计算进入分钟级时代
腾讯开源再次迎来重磅项目,14日,腾讯正式宣布开源高性能图计算框架Plato,这是在短短一周之内,开源的第五个重大项目. 相对于目前全球范围内其它的图计算框架,Plato可满足十亿级节点的超大规模图计 ...
- python基础-匿名函数和内置函数
匿名函数和内置函数 匿名函数:没有名字,使用一次即被收回,加括号就可以运行的函数. 语法:lambda 参数:返回值 使用方式: 将匿名函数赋值给变量,给匿名函数一个名字,使用这个变量来调用(还不如用 ...
- 【html css js】实现一个简易日历
——[效果预览] 实现了日历最基础的功能,当前日期红色显示,可通过上方的左右按钮查看上一月或下一月的日期. ——[代码部分] 1. HTML <body> <div class=&q ...
- Linux下RIAD的实现及mdadm命令的基本用法
一.RAID简述 磁盘阵列(Redundant Arrays of Independent Disks,RAID),是把多个物理磁盘组成一个阵列,当作一个逻辑磁盘使用,它将数据以分段或条带的方式储存在 ...
- C#语音特性 很实用
1.隐式类型 Var 2.匿名类型 new{} 3.自动属性 [prop TAB] public string Title { get; set; } 4.初始化器 var myObj1 = ne ...
- SqlServer2005 查询 第七讲 order by
今天我们来讲sql命令中的参数order by的用法 order by order by:可以理解成[以某个字段排序] order by a,b // a和b都按升序 order by a,b des ...
- Java基础:数组的声明,循环,赋值,拷贝。
数组的声明 一般有两种形式的声明: int[] a; int a[]; 这两种声明都没错,但是大多数都使用第一种声明,因为变量名是独立的,不带任何符号. 声明一个数组,并没有将a真正的初始化为一个数组 ...