为什么我们需要"应用程序健康监测".
很多问题是在其真正发生之前可以规避的, 我们的应用程序不可能一直稳定运行, 所以需要一定的检测机制去对其进行保驾护航. 在出现异常端倪之前今早发现问题, 排查问题.
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);