浅谈 asp.net core 中的 healthCheck 并结合 consul 改造 web api
本篇已收录至 asp.net core 随笔系列
为什么我们需要"应用程序健康监测".
很多问题是在其真正发生之前可以规避的, 我们的应用程序不可能一直稳定运行, 所以需要一定的检测机制去对其进行保驾护航. 在出现异常端倪之前今早发现问题, 排查问题.
link: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-3.1
能做什么
-
检测应用的状态, 并对无效的应用的状态做出一些措施.
-
对系统的内存使用, 磁盘空间使用和其他的物理资源的使用进行监控.
-
同样可以检测应用程序所依赖的服务的健康与否.
为什么
-
目前微服务架构是最火热的架构技术, 所以一般微服务中的单服务都会提供一个 health 接口, 用于 loadbalance 机制去检测服务节点是否还在正常工作.
-
单纯的接口如果只返回一个OK, 那就没意思了, 因为你的服务要保证业务正常的前提下返回OK才算真正意义上的OK.
-
所以在 anc 应用中配置健康监测需要监测的实际内容, 最后都没有问题了. 再返回OK, 负载均衡机制才会放心的把 traffic 发到你的实例中.
健康监测的原则
-
监测机制不要搞很久, 这样负载均衡每次ping你一次都得很久的话, 那样的监测实际上意义不大
-
一次检测尽量将结果返回的全一些
-
可以但不是必须持久化你的监测记录
-
不提供深度细节的检测
-
不提供额外的log
-
不提供用于debug的数据
anc的health check
-
anc框架在2.2以后提供了开箱即用的health check中间件. 只需要在startup.cs文件中配置如下简单的代码, 但是实际应用程序中要复杂的很多
// startup.cs 中 configureservice 方法, 添加 services.AddHealthChecks(); // startup.cs 中 configure 方法, 添加 var options = new HealthCheckOptions(); options.ResponseWriter = async (c, r) => { c.Response.ContentType = "application/json"; var result = JsonConvert.SerializeObject(new { status = r.Status.ToString(), errors = r.Entries.Select(e => new { key = e.Key, value = e.Value.Status.ToString() }) }); await c.Response.WriteAsync(result); }; app.UseHealthChecks("/health", options);
-
带有UI的例子: https://www.telerik.com/blogs/health-checks-in-aspnet-core
-
base 概念: https://gunnarpeipman.com/aspnet-core-health-checks/
结合 consul 来对 web api 健康监测
consul 是一个服务治理的开源工具. 本文不涉及其内容.
使用 consul 需要为你的 web api 项目添加 consul nuget package.
项目中添加一个 IApplicationBuilder extension 类, 代码如下:
using Consul;
using DennisBlog.WebAPI.Utils;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
using System;
namespace DennisBlog.WebAPI.Extensions
{
public static class AppBuilderExtension
{
public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IHostApplicationLifetime lifetime, ServiceEntity serviceEntity)
{
Action<ConsulClientConfiguration> action = (x=> x.Address = new Uri($"http://{serviceEntity.ConsulIP}:{serviceEntity.ConsulPort}"));
var consulClient = new ConsulClient(action);
var httpCheck = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔,或者称为心跳间隔
HTTP = $"http://{serviceEntity.IP}:{serviceEntity.Port}/health",//健康检查地址
Timeout = TimeSpan.FromSeconds(5)
};
// Register service with consul
var registration = new AgentServiceRegistration()
{
Checks = new[] { httpCheck },
ID = Guid.NewGuid().ToString(),
Name = serviceEntity.ServiceName,
Address = serviceEntity.IP,
Port = serviceEntity.Port,
Tags = new[] { $"urlprefix-/{serviceEntity.ServiceName}" }//添加 urlprefix-/servicename 格式的 tag 标签,以便 Fabio 识别
};
consulClient.Agent.ServiceRegister(registration).Wait();//服务启动时注册,内部实现其实就是使用 Consul API 进行注册(HttpClient发起)
lifetime.ApplicationStopping.Register(() =>
{
consulClient.Agent.ServiceDeregister(registration.ID).Wait();//服务停止时取消注册
});
return app;
}
}
}
修改 startup.cs 的 configure 方法 , 向 consul 注册你的 web api:
var ip = _configuration["ip"] ?? "localhost";
var port = Convert.ToInt32(_configuration["port"] ?? "8080");
var serviceName = _configuration["Service:Name"] ?? "Dennis.Blog.WebApiService";
var consulIp = _configuration["Consul:IP"] ?? "localhost";
var consulPort = Convert.ToInt32(_configuration["Consul:Port"] ?? "8500");
ServiceEntity service = new ServiceEntity
{
IP = ip,
Port = port,
ServiceName = serviceName,
ConsulIP = consulIp,
ConsulPort = consulPort
};
app.RegisterConsul(lifetime, service);