写在前面

我们的系统可能因为正在部署、服务异常终止或者其他问题导致系统处于非健康状态,这个时候我们需要知道系统的健康状况,而健康检查可以帮助我们快速确定系统是否处于正常状态。一般情况下,我们会提供公开的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(一)的更多相关文章

  1. .NET Core 3.0之深入源码理解Startup的注册及运行

    原文:.NET Core 3.0之深入源码理解Startup的注册及运行   写在前面 开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程 ...

  2. .NET Core 3.0之深入源码理解Configuration(一)

    Configuration总体介绍 微软在.NET Core里设计出了全新的配置体系,并以非常灵活.可扩展的方式实现.从其源码来看,其运行机制大致是,根据其Source,创建一个Builder实例,并 ...

  3. .NET Core 3.0之深入源码理解Kestrel的集成与应用(一)

      写在前面 ASP.NET Core 的 Web 服务器默认采用Kestrel,这是一个基于libuv(一个跨平台的基于Node.js异步I/O库)的跨平台.轻量级的Web服务器. 在开始之前,先回 ...

  4. .NET Core 3.0之深入源码理解Kestrel的集成与应用(二)

      前言 前一篇文章主要介绍了.NET Core继承Kestrel的目的.运行方式以及相关的使用,接下来将进一步从源码角度探讨.NET Core 3.0中关于Kestrel的其他内容,该部分内容,我们 ...

  5. .NET Core 3.0之深入源码理解HttpClientFactory(二)

      写在前面 上一篇文章讨论了通过在ConfigureServices中调用services.AddHttpClient()方法,并基于此进一步探讨了DefaultHttpClientFactory是 ...

  6. .NET Core 3.0之深入源码理解Host(二)

      写在前面 停了近一个月的技术博客,随着正式脱离996的魔窟,接下来也正式恢复了.本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点,主要讨论Long Run Pro ...

  7. .NET Core 3.0之深入源码理解ObjectPool(一)

    写在前面 对象池是一种比较常用的提高系统性能的软件设计模式,它维护了一系列相关对象列表的容器对象,这些对象可以随时重复使用,对象池节省了频繁创建对象的开销. 它使用取用/归还的操作模式,并重复执行这些 ...

  8. .NET Core 3.0之深入源码理解Host(一)

    写在前面 ASP .NET Core中的通用主机构建器是在v2.1中引入的,应用在启动时构建主机,主机作为一个对象用于封装应用资源以及应用程序启动和生存期管理.其主要功能包括配置初始化(包括加载配置以 ...

  9. .NET Core 3.0之深入源码理解Configuration(三)

      写在前面 上一篇文章讨论了文件型配置的基本内容,本篇内容讨论JSON型配置的实现方式,理解了这一种配置类型的实现方式,那么其他类型的配置实现方式基本可以触类旁通.看过了上一篇文章的朋友,应该看得出 ...

随机推荐

  1. Md5与HMAC-SHA256

    Md5签名算法 private string CalMd5(string str) { var md5 = MD5.Create(); var bs = md5.ComputeHash(Encodin ...

  2. GStreamer基础教程12 - 常用命令工具

    摘要 GStreamer提供了不同的命令行工具用于快速的查看信息以及验证Pipeline的是否能够正确运行,在平时的开发过程中,我们也优先使用GStreamer的命令行工具验证,再将Pipeline集 ...

  3. (IDEA) 搭建Maven并使用Maven打包部署

    1.配置Maven的环境变量 a.首先我们去maven官网下载Maven程序,解压到安装目录,如图所示: b.配置M2_HOME的环境变量,然后将该变量添加到Path中 备注:必须要有JAVA_HOM ...

  4. linux IMX6 汇编点亮一个LED灯

    驱动Linux引脚与驱动STM32其实是一样的,都是在操作寄存器,在相应的寄存器上附上相应的值即可驱动. IMX6U手册上有各个管脚的命名,跟STM32不同,IOMUXC_SW_MUC_CTL_PAD ...

  5. 使用Spring Data JPA进行数据分页与排序

    一.导读 如果一次性加载成千上万的列表数据,在网页上显示将十分的耗时,用户体验不好.所以处理较大数据查询结果展现的时候,分页查询是必不可少的.分页查询必然伴随着一定的排序规则,否则分页数据的状态很难控 ...

  6. 【原创】基于.NET的轻量级高性能 ORM - TZM.XFramework 之优雅增删改

    [前言] 大家好,我是TANZAME.出乎意料的,我们在立冬的前一天又见面了,天气慢慢转凉,朋友们注意添衣保暖,愉快撸码.距离 TZM.XFramework 的首秀已数月有余,期间收到不少朋友的鼓励. ...

  7. 对pwntools生成的exp模版做了一些修改

    安装pwntools后,有一些命令行的工具可以用 ~ pwn template -h usage: pwn template [-h] [--host HOST] [--port PORT] [--u ...

  8. kettle6.1如何连接mongodb

    . Kettle的结构图如下: 2.介绍各个组件详细情况 表输入:通常是你的sql语句,这个会Kettle基础知识的都会不介绍了 JSON Output如下: MogoDB output如下: 下面这 ...

  9. N42-qq-林友埙-第二周作业

    1.Linux发行版的系统目录名称命名规则及用途 2.文件的元数据信息有哪些,分别表示什么含义,如何查看?如何修改文件的时间戳信息 1.文件的元数据是指文件的属性.大小.创建时间.访问时间.属主属组等 ...

  10. 【RocketMQ源码学习】- 3. Client 发送同步消息

    本文较长,代码后面给了方法简图,希望给你帮助 发送的方式 同步发送 异步发送 消息的类型 普通消息 顺序消息 事务消息 发送同步消息的时序图 为了防止读者朋友嫌烦,可以看下时序图,后面我也会给出方法的 ...