.NET Core 3.0之深入源码理解HealthCheck(一)
寫(xiě)在前面
我們的系統(tǒng)可能因?yàn)檎诓渴?、服?wù)異常終止或者其他問(wèn)題導(dǎo)致系統(tǒng)處于非健康狀態(tài),這個(gè)時(shí)候我們需要知道系統(tǒng)的健康狀況,而健康檢查可以幫助我們快速確定系統(tǒng)是否處于正常狀態(tài)。一般情況下,我們會(huì)提供公開(kāi)的HTTP接口,用于專(zhuān)門(mén)化健康檢查。
NET Core提供的健康檢查庫(kù)包括Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions和Microsoft.Extensions.Diagnostics.HealthChecks。這兩個(gè)庫(kù)共同為我們提供了最基礎(chǔ)的健康檢查的解決方案,后面擴(kuò)展的組件主要有下面幾個(gè),本文不作其他說(shuō)明。
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健康檢查的抽象基礎(chǔ),從中我們可以看出這個(gè)庫(kù)的設(shè)計(jì)意圖。它提供了一個(gè)統(tǒng)一的接口IHealthCheck,用于檢查應(yīng)用程序中各個(gè)被監(jiān)控組件的狀態(tài),包括后臺(tái)服務(wù)、數(shù)據(jù)庫(kù)等。這個(gè)接口只有一個(gè)方法CheckHealthAsync,
該方法有一個(gè)參數(shù)是HealthCheckContext,它表示當(dāng)前健康檢查執(zhí)行時(shí)所關(guān)聯(lián)的上下文對(duì)象,它的返回值HealthCheckResult表示當(dāng)前健康檢查結(jié)束后所產(chǎn)生的被監(jiān)控組件的運(yùn)行狀態(tài)。
源碼如下所示:
public interface IHealthCheck{ Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default);}HealthCheckRegistration
HealthCheckContext里面只有一個(gè)成員就是HealthCheckRegistration實(shí)例。
而HealthCheckRegistration是一個(gè)相當(dāng)重要的對(duì)象,它體現(xiàn)了健康檢查需要關(guān)注和注意的地方,其內(nèi)部涉及到五個(gè)屬性,分別用于:
標(biāo)識(shí)健康檢查名稱(chēng)
創(chuàng)建IHealthCheck實(shí)例
健康檢查的超時(shí)時(shí)間(防止我們因?yàn)榻】禉z查而過(guò)多占用資源)
失敗狀態(tài)標(biāo)識(shí)
一個(gè)標(biāo)簽集合(可用于健康檢查過(guò)濾)
這五個(gè)屬性的相關(guān)源碼如下:
public Func<IServiceProvider, IHealthCheck> Factory{ get => _factory; set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _factory = value; }} public HealthStatus FailureStatus { get; set; } public TimeSpan Timeout{ get => _timeout; set { if (value <= TimeSpan.Zero && value != System.Threading.Timeout.InfiniteTimeSpan) { throw new ArgumentOutOfRangeException(nameof(value)); } _timeout = value; }} public string Name{ get => _name; set { if (value == null) { throw new ArgumentNullException(nameof(value)); } _name = value; }} public ISet<string> Tags { get; }HealthCheckResult
HealthCheckResult是一個(gè)結(jié)構(gòu)體,可以看出這里更多的是基于承擔(dān)數(shù)據(jù)存儲(chǔ)和性能問(wèn)題的考量。
HealthCheckResult用于表示健康檢查的相關(guān)結(jié)果信息,同樣的,通過(guò)該類(lèi),我們知道了健康檢查需要關(guān)注的幾個(gè)點(diǎn):
組件的當(dāng)前狀態(tài)
異常信息
友好的描述信息(不管是異常還是正常)
額外可描述當(dāng)前組件的鍵值對(duì),這是一個(gè)開(kāi)放式的屬性,方面我們記錄更多信息
該類(lèi)含有四個(gè)公共屬性,和三個(gè)方法,相關(guān)源碼如下:
public struct HealthCheckResult{ private static readonly IReadOnlyDictionary<string, object> _emptyReadOnlyDictionary = new Dictionary<string, object>();
public HealthCheckResult(HealthStatus status, string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null) { Status = status; Description = description; Exception = exception; Data = data ?? _emptyReadOnlyDictionary;}
public IReadOnlyDictionary<string, object> Data { get; }
public string Description { get; }
public Exception Exception { get; }
public HealthStatus Status { get; }
public static HealthCheckResult Healthy(string description = null, IReadOnlyDictionary<string, object> data = null){ return new HealthCheckResult(status: HealthStatus.Healthy, description, exception: null, data);}
public static HealthCheckResult Degraded(string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null){ return new HealthCheckResult(status: HealthStatus.Degraded, description, exception: exception, data);}
public static HealthCheckResult Unhealthy(string description = null, Exception exception = null, IReadOnlyDictionary<string, object> data = null){ return new HealthCheckResult(status: HealthStatus.Unhealthy, description, exception, data);}}
可以看出這個(gè)三個(gè)方法都是基于HealthStatus這個(gè)枚舉而創(chuàng)建不同狀態(tài)的HealthCheckResult實(shí)例,這個(gè)枚舉表達(dá)了健康檢查需要關(guān)注的幾種狀態(tài),健康、異常以及降級(jí)。
HealthStatus的源碼如下:
public enum HealthStatus{ Unhealthy = 0, Degraded = 1, Healthy = 2,}IHealthCheckPublisher
健康檢查功能本質(zhì)上是一種輪詢功能,需要定期執(zhí)行,.NET Core 抽象定期執(zhí)行的接口,即IHealthCheckPublisher,我們可以通過(guò)實(shí)現(xiàn)這個(gè)接口,并與我們自定義的定時(shí)功能相結(jié)合。
同時(shí),作為一次健康檢查,我們還需要關(guān)注相關(guān)的健康檢查報(bào)告,那么我們需要關(guān)注那些點(diǎn)呢?
額外可描述當(dāng)前組件的鍵值對(duì),這是一個(gè)開(kāi)放式的屬性,方面我們記錄更多信息
友好的描述信息(不管是異常還是正常)
組件的當(dāng)前狀態(tài)
異常信息
當(dāng)前這次檢查所耗費(fèi)的時(shí)間
相關(guān)的標(biāo)簽信息
HealthReportEntry表示單個(gè)健康檢查報(bào)告,HealthReport表示一組健康檢查報(bào)告。HealthReport內(nèi)部維護(hù)了一個(gè)HealthReportEntry的字典數(shù)據(jù),HealthReport源碼如下所示:
public sealed class HealthReport{ public HealthReport(IReadOnlyDictionary<string, HealthReportEntry> entries, TimeSpan totalDuration) { Entries = entries; Status = CalculateAggregateStatus(entries.Values); TotalDuration = totalDuration; }public IReadOnlyDictionary<string, HealthReportEntry> Entries { get; }
public HealthStatus Status { get; }
public TimeSpan TotalDuration { get; }
private HealthStatus CalculateAggregateStatus(IEnumerable<HealthReportEntry> entries) { var currentValue = HealthStatus.Healthy; foreach (var entry in entries) { if (currentValue > entry.Status) { currentValue = entry.Status; }
if (currentValue == HealthStatus.Unhealthy) { // Game over, man! Game over! // (We hit the worst possible status, so there's no need to keep iterating) return currentValue; } }
return currentValue; }}
總結(jié)
通過(guò)以上內(nèi)容,我們知道了,一個(gè)完整的健康檢查需要關(guān)注健康檢查上下文、健康狀態(tài)的維護(hù)、健康檢查結(jié)果、健康檢查報(bào)告,同時(shí),為了更好的維護(hù)健康檢查,我們可以將健康檢查發(fā)布抽象出來(lái),并與外部的定時(shí)器相結(jié)合,共同守護(hù)健康檢查程序。
總結(jié)
以上是生活随笔為你收集整理的.NET Core 3.0之深入源码理解HealthCheck(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++模版和C#泛型求同存异录(一)si
- 下一篇: [ASP.NET Core 3框架揭秘]