开始使用ASP.NET核心运行状况检查

 

我现在刚接触 ASP.NET CoreHealthCheck中间件HealthCheck中间件用于在站点上公开运行一系列预先编写的测试的端点,并为您提供有关应用程序运行状况的快速诊断信息。如何使用这些示例:

  • 检查其状态超出您控制的外部的依赖(如第三方web服务和rest服务)的状态。
  • 检查数据依赖项的状态,例如SQL Server数据库和缓存服务器。
  • 检查您自己站点上端点的时间安排和性能。
  • 检查某些设置的值,这些设置在多环境设置中可能很难确定。
  • 检查某些诊断信息,例如磁盘和内存使用情况。

因此,让我们开始学习。在github存储库中Web项目中,我们要检查的第一件事是我编写的快速帮助程序类,该类将完成本教程的大部分繁重工作。在文件HealthChecks/Checks.cs中,有一个HealthCheckHelpers类。

public static async Task<HealthCheckResult> GenerateHealthCheckResultFromPIngRequest
                                            (string hostName)
      {
          using (var thePing = new Ping())
          {
              var pingResult = await thePing.SendPingAsync(hostName);
              var description = $"A ping of the {hostName} website";
              var healthCheckData = new Dictionary<string, object>();
              healthCheckData.Add("RoundTripMS", pingResult.RoundtripTime);
              healthCheckData.Add("ActualIPAddress", pingResult.Address.ToString());
              if (pingResult.Status == IPStatus.Success)
              {
                  return HealthCheckResult.Healthy(description, healthCheckData);
              }

              return HealthCheckResult.Unhealthy(description, null, healthCheckData);
          }
      }

我们要看的第一件事是函数GenerateHealthCheckResultFromPingRequest。此函数接收给定的主机,并将对其执行ping操作。如果它能够与主机通信,它将返回正常的报告并以毫秒为单位给出往返时间。如果不能,它将通过发布不健康的报告来通知用户。此类HealthCheck对于确定部署的站点是否可以与外部资源进行通信非常有用。这在服务器或主机环境可能已严格维护允许的主机和端口通信的严密安全情况下非常有用。

public static async Task<HealthCheckResult> RouteTimingHealthCheck(string routePath)
       {
           using (var client = new System.Net.WebClient())
           {
               var watch = new Stopwatch();
               watch.Start();
               var url = $"{BaseUrl}{routePath}";
               var result = await client.DownloadStringTaskAsync(url);
               watch.Stop();
               var milliseconds = watch.ElapsedMilliseconds;
               var healthCheckData = new Dictionary<string, object>();
               healthCheckData.Add("TimeInMS", milliseconds);
               if (milliseconds <= 1000)
                   return HealthCheckResult.Healthy
                          ($"call to  the route {routePath}", healthCheckData);
               else if (milliseconds >= 1001 && milliseconds <= 2000)
                   return HealthCheckResult.Degraded
                          ($"call to  the route {routePath}", null, healthCheckData);
               else
                   return HealthCheckResult.Unhealthy
                          ($"call to  the route {routePath}", null, healthCheckData);
           }
       }

下一个功能块是函数RouteTimingHealthCheck。此函数将从API路由并执行对其的调用。这将检查调用的响时间,然后根据时间分配健康状态。

       [HttpGet]
       public IEnumerable<WeatherForecast> Get()
       {
           var rng = new Random();
           return Enumerable.Range(1, 5).Select(index => new WeatherForecast
           {
               Date = DateTime.Now.AddDays(index),
               TemperatureC = rng.Next(-20, 55),
               Summary = Summaries[rng.Next(Summaries.Length)]
           })
           .ToArray();
       }

       [HttpGet("slow")]
       public async Task<string> Slow()
       {
           await Task.Delay(3000);
           return "slow";
       }

在文件Controllers/WeatherForecastController.cs,我们将使用controller动作作为默认GET方法,该方法应该会带来非常快速的健康测试结果,并且我们编写了一个标志为slow的函数,当调用该函数时会导致不健康的测试场景。最后,在助手类中,我们有:

public static async Task WriteResponses(HttpContext context, HealthReport result)
      {
          var json = new JObject(
                          new JProperty("status", result.Status.ToString()),
                          new JProperty("results", new JObject(result.Entries.Select(pair =>
                          new JProperty(pair.Key, new JObject(
                              new JProperty("status", pair.Value.Status.ToString()),
                              new JProperty("description", pair.Value.Description),
                              new JProperty("data", new JObject(pair.Value.Data.Select(
                                  p => new JProperty(p.Key, p.Value))))))))));

          context.Response.ContentType = MediaTypeNames.Application.Json;
          await context.Response.WriteAsync(json.ToString(Formatting.Indented));
      }

函数直接从MSDN复制,用于HealthCheck中间件。它将简单地覆盖默认响应编写器以进行运行状况检查并以干净的JSON响应格式。

使用这些函数的设置非常简单。首先,对Startup类中ConfigureServices方法的更改:

public void ConfigureServices(IServiceCollection services)
      {
          services.AddControllers();
          services.AddHealthChecks()
           .AddAsyncCheck("google_ping_check",
           () => HealthCheckHelpers.GenerateHealthCheckResultFromPIngRequest("google.com"))
           .AddAsyncCheck("microsoft_ping_check",
           () => HealthCheckHelpers.GenerateHealthCheckResultFromPIngRequest("microsoft.com"))
           .AddAsyncCheck("yahoo_ping_check",
           () => HealthCheckHelpers.GenerateHealthCheckResultFromPIngRequest("yahoo.com"))
           .AddAsyncCheck("localhost_ping_check",
           () => HealthCheckHelpers.GenerateHealthCheckResultFromPIngRequest("localhost"))
           .AddAsyncCheck("forecast_time_check",
           () => HealthCheckHelpers.RouteTimingHealthCheck("/weatherforecast"))
           .AddAsyncCheck("forecast_time_check_slow",
           () => HealthCheckHelpers.RouteTimingHealthCheck("/weatherforecast/slow"))
           .AddDbContextCheck<MyDatabaseContext>
           ("database_check", tags: new[] { "database" });
          services.AddHttpContextAccessor();

          services.AddDbContext<MyDatabaseContext>(options =>
          {
              options.UseSqlServer(@"Connection_string_here");
          });
      }

查看扩展方法AddAsyncCheck,我们看到它允许我们为健康检查传递名称。接下来,该函数接受一个返回Task<HealthCheckResult>Func参数。最后一个可选参数是适用于给定测试的标签列表。这使我们可以筛选并有条件地调用某些端点公开的某些测试。

services.AddHealthChecks()
        .AddAsyncCheck("google_ping_check",
         () => HealthCheckHelpers.GenerateHealthCheckResultFromPIngRequest("google.com"))
        .AddAsyncCheck("microsoft_ping_check",
         () => HealthCheckHelpers.GenerateHealthCheckResultFromPIngRequest("microsoft.com"))
        .AddAsyncCheck("yahoo_ping_check",
         () => HealthCheckHelpers.GenerateHealthCheckResultFromPIngRequest("yahoo.com"))
        .AddAsyncCheck("localhost_ping_check",
         () => HealthCheckHelpers.GenerateHealthCheckResultFromPIngRequest("localhost"))

在前四个测试中,我们使用ping检查将调用一些知名站点并尝试ping它们。

.AddAsyncCheck("forecast_time_check", 
() => HealthCheckHelpers.RouteTimingHealthCheck("/weatherforecast"))
                .AddAsyncCheck("forecast_time_check_slow", 
                () => HealthCheckHelpers.RouteTimingHealthCheck("/weatherforecast/slow"))

接下来,我们使用路由计时测试来检查我们自己的一些API端点。我们将故意将slow的端点称为强制不健康的响应。

扫描二维码关注公众号,回复: 9016281 查看本文章
.AddDbContextCheck<MyDatabaseContext>("database_check", tags: new[] { "database" });
...
 services.AddDbContext<MyDatabaseContext>(options =>
            {
                options.UseSqlServer(@"Connection_string_here");
            });

最后,我们有一个示例,说明设置数据库运行状况检查有多么容易。我们为空的EF上下文设置了一个错误的连接字符串来设置它。当运行状况检查尝试与错误的数据库连接通信时,这将强制执行不正常的响应。您可以使用合法的数据库连接字符串进行设置,并查看运行状况检查。

下一部分将显示我们对Configure函数的更改:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
       {
           app.Use(async (context, next) =>
           {
               HealthCheckHelpers.BaseUrl =
                     $"{ context.Request.Scheme}://{context.Request.Host}";
               await next();
           });

           app.UseHealthChecks("/health",
              new HealthCheckOptions
              {
                  ResponseWriter = HealthCheckHelpers.WriteResponses
              });

           if (env.IsDevelopment())
           {
               app.UseDeveloperExceptionPage();
           }

           app.UseHttpsRedirection();

           app.UseRouting();

           app.UseAuthorization();

           app.UseEndpoints(endpoints =>
           {
               endpoints.MapHealthChecks("/health/database", new HealthCheckOptions
               {
                   Predicate = (check) => check.Tags.Contains("database"),
                   ResponseWriter = HealthCheckHelpers.WriteResponses
               });

               endpoints.MapHealthChecks("/health/websites", new HealthCheckOptions
               {
                   Predicate = (check) => check.Tags.Contains("websites"),
                   ResponseWriter = HealthCheckHelpers.WriteResponses
               });

               endpoints.MapControllers();
           });
       }
   }

我们进行了很少的更改来设置健康检查设置。

app.UseHealthChecks("/health",
              new HealthCheckOptions
              {
                  ResponseWriter = HealthCheckHelpers.WriteResponses
              });

第一个值得注意的变化是添加了UseHealthChecks扩展方法,该方法将给定的端点添加到中间件集合中。当站点运行时,在这种情况下它将公开一个端点/health,它将执行ConfigureServices方法中的测试设置。在没有提供任何条件的情况下,此端点将执行所有给定的测试。接下来,我们设置HealthCheckOptions对象的ResponseWriter属性。我们将其设置为我们的帮助器类中的writer方法,该方法会将响应更改为JSON

app.UseEndpoints(endpoints =>
            {
                endpoints.MapHealthChecks("/health/database", new HealthCheckOptions
                {
                    Predicate = (check) => check.Tags.Contains("database"),
                    ResponseWriter = HealthCheckHelpers.WriteResponses
                });

                endpoints.MapHealthChecks("/health/websites", new HealthCheckOptions
                {
                    Predicate = (check) => check.Tags.Contains("websites"),
                    ResponseWriter = HealthCheckHelpers.WriteResponses
                });

                endpoints.MapControllers();
            });

接下来,在UseEndpoints扩展方法的实现中,我们设置了用于过滤运行状况检查的端点。谓词属性采用一个函数,该函数返回要对其执行运行状况检查的筛选器。在此示例中,我们设置了端点,这些端点将根据分配给它们的标签过滤测试。

设置完成后,我们可以运行该项目并将浏览器指向/health端点:

我们应该得到类似于以下响应的json响应:

您将结果视为HealthCheckResult对象数组。它们将具有其分配的名称,并将具有我们分配为键和值的字典对象的其他自定义属性,并将具有描述它们是否健康,性能下降或不健康的属性。

健康检查不仅可以像我们一样内联设置,而且对于更广泛的测试,可以将它们设置为实现IHealthCheck接口的类。

结论

我们仅在此处进行了介绍,但如您所见,这种非常有用的中间件的可能性是无限的。可以在下面找到有关HealthCheck中间件的其他信息:

发布了69 篇原创文章 · 获赞 133 · 访问量 44万+

猜你喜欢

转载自blog.csdn.net/mzl87/article/details/104207078