.NET Core 3.1之深入源码理解HealthCheck(二)
2021-04-30 11:28
标签:provider app start padding 依赖项 接口 abs col case 前文讨论了HealthCheck的理论部分,本文将讨论有关HealthCheck的应用内容。 在应用中引入HealthCheck,一般需要配置Startup文件,如下所示: 其中services.AddHealthChecks();会把我们引入到HealthCheckService的扩展方法中,代码如下: 该扩展方法会尝试注册一个HealthCheckService的单例对象。HealthCheckService本身是一个抽象类,它内部含有一个抽象方法,主要用于执行健康检查并返回健康状态的聚合信息。抽象方法如下所示: HealthCheckService有一个默认派生类,就是DefaultHealthCheckService,在其构造方法中,会去验证是否有重复的健康检查名称存在,如果有,就会抛出异常。另外名称的检查是不区分大小写的。该类所实现的抽象方法作为健康检查的核心功能,内部实现还是比较复杂的。 首先我们看一下该方法的实现源码: 1、其内部有比较完善的监控机制,会在内部维护了一个Log功能,全程监控健康检查的耗时,该日志所记录的健康检查不仅仅是一个健康检查集合的耗时,而且也记录了每个Name的耗时。 2、该方法会通过await Task.WhenAll(tasks).ConfigureAwait(false);并发执行健康检查。当然,我需要注意的是,过多的健康检查任务将会导致系统性能的下降,这主要看如何取舍了 CheckHealthAsync内部还会调用一个私有方法RunCheckAsync,这是真正执行健康检查的方法。RunCheckAsync方法执行完成后,会创建HealthReportEntry对象返回到CheckHealthAsync中,并组装到HealthReport对象中,到此该抽象方法执行完毕。 以下是RunCheckAsync方法的源码?: .NET Core 3.1之深入源码理解HealthCheck(二) 标签:provider app start padding 依赖项 接口 abs col case 原文地址:https://www.cnblogs.com/edison0621/p/12152475.html写在前面
源码研究
1: public void ConfigureServices(IServiceCollection services)
2: {
3: services.AddHealthChecks();
4: }
5:
6: public void Configure(IApplicationBuilder app)
7: {
8: app.UseRouting();
9:
10: app.UseEndpoints(endpoints =>
11: {
12: endpoints.MapHealthChecks("/health");
13: });
14: }
1: public static class HealthCheckServiceCollectionExtensions
2: {
3: public static IHealthChecksBuilder AddHealthChecks(this IServiceCollection services)
4: {
5: services.TryAddSingleton
6: services.TryAddEnumerable(ServiceDescriptor.Singleton
7: return new HealthChecksBuilder(services);
8: }
9: }
1: public abstract Task
2: Func
3: CancellationToken cancellationToken = default);
1: public override async Task
2: Func
3: CancellationToken cancellationToken = default)
4: {
5: var registrations = _options.Value.Registrations;
6: if (predicate != null)
7: {
8: registrations = registrations.Where(predicate).ToArray();
9: }
10:
11: var totalTime = ValueStopwatch.StartNew();
12: Log.HealthCheckProcessingBegin(_logger);
13:
14: var tasks = new Task
15: var index = 0;
16: using (var scope = _scopeFactory.CreateScope())
17: {
18: foreach (var registration in registrations)
19: {
20: tasks[index++] = Task.Run(() => RunCheckAsync(scope, registration, cancellationToken), cancellationToken);
21: }
22:
23: await Task.WhenAll(tasks).ConfigureAwait(false);
24: }
25:
26: index = 0;
27: var entries = new Dictionarystring, HealthReportEntry>(StringComparer.OrdinalIgnoreCase);
28: foreach (var registration in registrations)
29: {
30: entries[registration.Name] = tasks[index++].Result;
31: }
32:
33: var totalElapsedTime = totalTime.GetElapsedTime();
34: var report = new HealthReport(entries, totalElapsedTime);
35: Log.HealthCheckProcessingEnd(_logger, report.Status, totalElapsedTime);
36: return report;
37: }
1: private async Task
2: {
3: cancellationToken.ThrowIfCancellationRequested();
4:
5: var healthCheck = registration.Factory(scope.ServiceProvider);
6:
7: using (_logger.BeginScope(new HealthCheckLogScope(registration.Name)))
8: {
9: var stopwatch = ValueStopwatch.StartNew();
10: var context = new HealthCheckContext { Registration = registration };
11:
12: Log.HealthCheckBegin(_logger, registration);
13:
14: HealthReportEntry entry;
15: CancellationTokenSource timeoutCancellationTokenSource = null;
16: try
17: {
18: HealthCheckResult result;
19:
20: var checkCancellationToken = cancellationToken;
21: if (registration.Timeout > TimeSpan.Zero)
22: {
23: timeoutCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
24: timeoutCancellationTokenSource.CancelAfter(registration.Timeout);
25: checkCancellationToken = timeoutCancellationTokenSource.Token;
26: }
27:
28: result = await healthCheck.CheckHealthAsync(context, checkCancellationToken).ConfigureAwait(false);
29:
30: var duration = stopwatch.GetElapsedTime();
31:
32: entry = new HealthReportEntry(
33: status: result.Status,
34: description: result.Description,
35: duration: duration,
36: exception: result.Exception,
37: data: result.Data,
38: tags: registration.Tags);
39:
40: Log.HealthCheckEnd(_logger, registration, entry, duration);
41: Log.HealthCheckData(_logger, registration, entry);
42: }
43: catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested)
44: {
45: var duration = stopwatch.GetElapsedTime();
46: entry = new HealthReportEntry(
47: status: HealthStatus.Unhealthy,
48: description: "A timeout occured while running check.",
49: duration: duration,
50: exception: ex,
51: data: null);
52:
53: Log.HealthCheckError(_logger, registration, ex, duration);
54: }
55:
56: // Allow cancellation to propagate if it‘s not a timeout.
57: catch (Exception ex) when (ex as OperationCanceledException == null)
58: {
59: var duration = stopwatch.GetElapsedTime();
60: entry = new HealthReportEntry(
61: status: HealthStatus.Unhealthy,
62: description: ex.Message,
63: duration: duration,
64: exception: ex,
65: data: null);
66:
67: Log.HealthCheckError(_logger, registration, ex, duration);
68: }
69:
70: finally
71: {
72: timeoutCancellationTokenSource?.Dispose();
73: }
74:
75: return entry;
76: }
77: }
来自官方的应用
其他更多内容请参考:https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-3.1select 1 from
tableName
根据数据库响应来判断是否健康
DbContext
检查确认应用可以与为 EF Core DbContext
配置的数据库通信。
上一篇:Flask和Ajax交互
文章标题:.NET Core 3.1之深入源码理解HealthCheck(二)
文章链接:http://soscw.com/essay/80383.html