写在前面

前文讨论了HealthCheck的理论部分,本文将讨论有关HealthCheck的应用内容。

  • 可以监视内存、磁盘和其他物理服务器资源的使用情况来了解是否处于正常状态。

  • 运行状况检查可以测试应用的依赖项(如数据库和外部服务终结点)以确认是否可用和正常工作。

  • 运行状况探测可以由容器业务流程协调程序和负载均衡器用于检查应用的状态。

源码研究

在应用中引入HealthCheck,一般需要配置Startup文件,如下所示:

public void ConfigureServices(IServiceCollection services)
{services.AddHealthChecks();
}public void Configure(IApplicationBuilder app)
{app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapHealthChecks("/health");});
}

其中services.AddHealthChecks();会把我们引入到HealthCheckService的扩展方法中,代码如下:

public static class HealthCheckServiceCollectionExtensions
{public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services){services.TryAddSingleton<HealthCheckService, DefaultHealthCheckService>();services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, HealthCheckPublisherHostedService>());return new HealthChecksBuilder(services);}
}

该扩展方法会尝试注册一个HealthCheckService的单例对象。HealthCheckService本身是一个抽象类,它内部含有一个抽象方法,主要用于执行健康检查并返回健康状态的聚合信息。抽象方法如下所示:

public abstract Task<HealthReport> CheckHealthAsync(Func<HealthCheckRegistration, bool> predicate,CancellationToken cancellationToken = default);

HealthCheckService有一个默认派生类,就是DefaultHealthCheckService,在其构造方法中,会去验证是否有重复的健康检查名称存在,如果有,就会抛出异常。另外名称的检查是不区分大小写的。该类所实现的抽象方法作为健康检查的核心功能,内部实现还是比较复杂的。

首先我们看一下该方法的实现源码:

public override async Task<HealthReport> CheckHealthAsync(Func<HealthCheckRegistration, bool> predicate,CancellationToken cancellationToken = default)
{var registrations = _options.Value.Registrations;if (predicate != null){registrations = registrations.Where(predicate).ToArray();}var totalTime = ValueStopwatch.StartNew();Log.HealthCheckProcessingBegin(_logger);var tasks = new Task<HealthReportEntry>[registrations.Count];var index = 0;using (var scope = _scopeFactory.CreateScope()){foreach (var registration in registrations){tasks[index++] = Task.Run(() => RunCheckAsync(scope, registration, cancellationToken), cancellationToken);}await Task.WhenAll(tasks).ConfigureAwait(false);}index = 0;var entries = new Dictionary<string, HealthReportEntry>(StringComparer.OrdinalIgnoreCase);foreach (var registration in registrations){entries[registration.Name] = tasks[index++].Result;}var totalElapsedTime = totalTime.GetElapsedTime();var report = new HealthReport(entries, totalElapsedTime);Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime);return report;
}

1、其内部有比较完善的监控机制,会在内部维护了一个Log功能,全程监控健康检查的耗时,该日志所记录的健康检查不仅仅是一个健康检查集合的耗时,而且也记录了每个Name的耗时。

2、该方法会通过await Task.WhenAll(tasks).ConfigureAwait(false);并发执行健康检查。当然,我需要注意的是,过多的健康检查任务将会导致系统性能的下降,这主要看如何取舍了

CheckHealthAsync内部还会调用一个私有方法RunCheckAsync,这是真正执行健康检查的方法。RunCheckAsync方法执行完成后,会创建HealthReportEntry对象返回到CheckHealthAsync中,并组装到HealthReport对象中,到此该抽象方法执行完毕。

以下是RunCheckAsync方法的源码:

private async Task<HealthReportEntry> RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration, CancellationToken cancellationToken)
{cancellationToken.ThrowIfCancellationRequested();var healthCheck = registration.Factory(scope.ServiceProvider);using (_logger.BeginScope(new HealthCheckLogScope(registration.Name))){var stopwatch = ValueStopwatch.StartNew();var context = new HealthCheckContext { Registration = registration };Log.HealthCheckBegin(_logger, registration);HealthReportEntry entry;CancellationTokenSource timeoutCancellationTokenSource = null;try{HealthCheckResult result;var checkCancellationToken = cancellationToken;if (registration.Timeout > TimeSpan.Zero){timeoutCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);timeoutCancellationTokenSource.CancelAfter(registration.Timeout);checkCancellationToken = timeoutCancellationTokenSource.Token;}result = await healthCheck.CheckHealthAsync(context, checkCancellationToken).ConfigureAwait(false);var duration = stopwatch.GetElapsedTime();entry = new HealthReportEntry(status: result.Status,description: result.Description,duration: duration,exception: result.Exception,data: result.Data,tags: registration.Tags);Log.HealthCheckEnd(_logger, registration, entry, duration);Log.HealthCheckData(_logger, registration, entry);}catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested){var duration = stopwatch.GetElapsedTime();entry = new HealthReportEntry(status: HealthStatus.Unhealthy,description: "A timeout occured while running check.",duration: duration,exception: ex,data: null);Log.HealthCheckError(_logger, registration, ex, duration);}// Allow cancellation to propagate if it's not a timeout.catch (Exception ex) when (ex as OperationCanceledException == null){var duration = stopwatch.GetElapsedTime();entry = new HealthReportEntry(status: HealthStatus.Unhealthy,description: ex.Message,duration: duration,exception: ex,data: null);Log.HealthCheckError(_logger, registration, ex, duration);}finally{timeoutCancellationTokenSource?.Dispose();}return entry;}
}

来自官方的应用

  • 数据库探测,例子可以是执行select 1 from tableName根据数据库响应来判断是否健康

  • Entity Framework Core DbContext 探测,DbContext 检查确认应用可以与为 EF Core DbContext 配置的数据库通信。

  • 单独的就绪情况和运行情况探测,在某些托管方案中,可能初始化是一个比较耗时的操作,应用正常运行,但是可能还不能正常处理请求并响应

  • 具有自定义响应编写器的基于指标的探测,比如检查内存占用是否超标,cpu 是否占用过高,连接数是否达到上限

  • 按端口筛选,指定端口,一般用于容器环境,根据容器启动时配置的端口号进行响应

  • 分发运行状况检查库,将检查接口实现独立一个类,并通过依赖注入获取参数,检查时根据参数编写逻辑

  • 运行状况检查发布服务器,如果向 DI 添加 IHealthCheckPublisher,则运行状态检查系统将定期执行状态检查,并使用结果调用 PublishAsync。适用于需要推送的健康系统,而不是健康系统

  • 使用 MapWhen 限制运行状况检查,使用 MapWhen 对运行状况检查终结点的请求管道进行条件分支

其他更多内容请参考:https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-3.1

.NET Core 3.1之深入源码理解HealthCheck(二)相关推荐

  1. .NET Core 3.0之深入源码理解HealthCheck(一)

    写在前面 我们的系统可能因为正在部署.服务异常终止或者其他问题导致系统处于非健康状态,这个时候我们需要知道系统的健康状况,而健康检查可以帮助我们快速确定系统是否处于正常状态.一般情况下,我们会提供公开 ...

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

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

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

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

  4. faster rcnn源码理解(二)之AnchorTargetLayer(网络中的rpn_data)

    转载自:faster rcnn源码理解(二)之AnchorTargetLayer(网络中的rpn_data) - 野孩子的专栏 - 博客频道 - CSDN.NET http://blog.csdn.n ...

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

    开发.NET Core应用,直接映入眼帘的就是Startup类和Program类,它们是.NET Core应用程序的起点.通过使用Startup,可以配置化处理所有向应用程序所做的请求的管道,同时也可 ...

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

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

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

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

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

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

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

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

最新文章

  1. python3 操作mysql数据库(mysql.connector 和 pymysql )
  2. 驱动设计的思想:面向对象/分层/分离
  3. 如何给网红变现?Instagram正在尝试一种全新的方式
  4. 转载|pymysql.err.InternalError: Packet sequence number wrong - got 45 expected 0
  5. C/C++越来越不行了?让我们看看C++的未来趋势
  6. 【Windows 命令行】CMD 在资源管理器中打开当前路径
  7. 4059. 统计某指定字符出现个数
  8. OpenStack日志搜集分析之ELK
  9. 利用token 防止表单重复提交
  10. 基于Java的学生成绩管理系统
  11. java实现gps定位_GPS定位数据的提取与存储系统的设计
  12. 解决谷歌浏览器自动填充表单
  13. 这家自动驾驶公司,或将引领半封闭物流搬运领域的变革
  14. 常见存储的串口连接管理方法
  15. c# NPOI按模板导出
  16. php和jsp和asp的区别,php asp jsp 三者的区别是什么
  17. 常用的文件后缀都有哪些?
  18. Canvas笔画向量交互动画效果,随着鼠标描绘轨迹
  19. Java IO Steam
  20. Qt模仿QQ聊天窗口界面(一)

热门文章

  1. Hibernate注解之@Enumerated
  2. airdrop 是 蓝牙吗_您可以在Windows PC或Android手机上使用AirDrop吗?
  3. 如何在Mac上设置FaceTime
  4. 您可能没有注意到的7个Ubuntu File Manager功能
  5. Mycat 之 通过Keepalived 实现高可用
  6. Atitit.ati orm的设计and架构总结 适用于java c# php版
  7. java提示找不到或无法加载主类
  8. Linux入门之进程管理(4)之进程与文件
  9. Apache-Jmeter监控服务资源
  10. HP产品选件查询网站