ASP .NET Core App.Metrics+InfluxDB+Grafana性能监控

Grafana介绍及部署请参考这篇博客

InfluxDB

InfluxDB介绍

InfluxDB 是用Go语言编写的一个开源分布式时序、事件和指标数据库,无需外部依赖
InfluxDB在DB-Engines的时序数据库类别里排名第一

InfluxDB相关概念

  • Database:InfluxDB可以创建数据库,一个数据库可以包含多个user、保存策略、schemaless ,支持随时灵活创建mersurement
  • Measurement:相当于表的概念;
  • Tags:是一些kv的结构,标签会被用来建立索引;
  • Fields:是保存真实数据的结构,也是kv结构,但是不会被用来建立索引;
  • Point: 代表了一条记录,可以理解为关系型数据库中的一条记录;
  • Timestamp:既然InfluxDB被称之为时序数据库,少了时间是不可能的,每条记录必须要有一个时间戳;
  • Series:是由Measurement+Tags组成的

InfluxDB优点

InfluxDB 自带的各种特殊函数如求标准差,随机取样数据,统计数据变化比等,使数据统计和实时分析变得十分方便。 此外它还有如下特性:

  • 内置 HTTP 接口,使用方便
  • 数据可以打标记,这样查询可以很灵活
  • 类 SQL 的查询语句
  • 安装管理很简单,并且读写数据很高效
  • 能够实时查询,数据在写入时被索引后就能够被立即查出

InfluxDB版本

InfluxDB目前推出了2.0版本,由于改动较大,所以和1.x版本并存。目前官方推荐的 稳定版本依旧是1.x版本。2.0主要的更改包括以下内容:

  • 集成了TICK组件,一键安装
  • 安全集成,所有的请求都需要通过token
  • 集成管理页面,支持更为强大的统计和分析功能
  • 支持新的查询语言Flux,提供更为强大的查询和处理功能
  • 增加了面向IoT和边缘计算的功能,能够在 ingestion point 汇总和分析时间序列数据
  • 启动了新的存储引擎InfluxDB Iox,采用Rust语言编写

部署

新建配置和存储目录并写入权限

mkdir -p /dockerdata/influxdb/{config,data}
chown -R 1000:1000   /dockerdata/influxdb
chmod -R 777  /dockerdata/influxdb

Docker部署1.0 InfluxDB

docker pull influxdb:1.8

#说明: 端口8083:web访问端口;端口8086:数据写入端口。
docker run -d -p 8086:8086  -p 8083:8083 --name influxdb -v /dockerdata/influxdb/data/influxdb:/var/lib/influxdb    influxdb:1.8

进入容器使用数据库命令

#进入容器
docker exec -it  influxdb  bash
cd /usr/bin
#进入数据库
./influx

#创建数据库
create databases my_influxdb
#查看所有数据库
show databases

默认不开启Web UI和数据库认证,访问http://192.168.1.5:8086/会报404。

Docker部署2.0 InfluxDB

#拉最新版镜像
docker pull influxdb

#主机上生成配置文件
docker run --rm influxdb2   influxd print-config > /dockerdata/influxdb/config/config.yml

#Run站点
# influxdb 2.X 版本存储目录在 /var/lib/influxdb2

docker run -d -p 8086:8086 --name influxdb2   \
  -v /dockerdata/influxdb/config/config.yml:/etc/influxdb2/config.yml \
  -v /dockerdata/influxdb/data/influxdb2:/var/lib/influxdb2 \
  -v /etc/localtime:/etc/localtime:ro \
   influxdb

浏览器访问http://192.168.1.5:8086/,配置管理员账号密码和组织信息

快速开始

进入首页

查看管理员Token

ASP .NET Core 集成 App.Metrics

Neget安装以下包

App.Metrics.AspNetCore.All

#InfluxDB 1.0 安装
App.Metrics.InfluxDB

#InfluxDB 2.0 安装
AK.App.Metrics.Reporting.InfluxDB2

appsettings.json填入相关配置信息

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "MetricsOptions": {
    "DefaultContextLabel": "test_service",
    //"GlobalTags": {
    //  "app": "test_service123",
    //  "env": "prd444"
    //},
    "ServerTag": "192.168.1.6",
    "AppTag": "app2",
    "EnvTag": "env2",
    "Enabled": true
  },
  "MetricsWebTrackingOptions": {
    "ApdexTrackingEnabled": true,
    "ApdexTSeconds": 0.1,
    "IgnoredHttpStatusCodes": [ 302, 404 ],
    "IgnoredRoutesRegexPatterns": [],
    "OAuth2TrackingEnabled": true
  },
  "MetricEndpointsOptions": {
    "MetricsEndpointEnabled": true,
    "MetricsTextEndpointEnabled": true,
    "EnvironmentInfoEndpointEnabled": true
  },
  "MetricsReportingInfluxDbOptions": {
    "InfluxDb": {
      "CreateDataBaseIfNotExists": true,
      "BaseUri": "http://192.168.1.5:8086",
      "Database": "mydb",
      //"UserName": "rdcmonitor",
      //"Password": ":sXpvRxgxe*38"
    },
    "FlushInterval": "00:00:20"
  },
  "AllowedHosts": "*"
}

Program.cs添加配置

using App.Metrics;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

//AddMetrics

builder.Services.AddControllers().AddMetrics();

var _configuration = builder.Configuration;

var metrics = AppMetrics.CreateDefaultBuilder().Configuration.Configure(options =>
{
    options.DefaultContextLabel = _configuration["MetricsOptions:DefaultContextLabel"];

    //options.GlobalTags = new GlobalMetricTags(Configuration.GetSection("MetricsOptions:GlobalTags").Get<Dictionary<string, string>>());
    options.AddServerTag(_configuration["MetricsOptions:ServerTag"]);
    options.AddAppTag(_configuration["MetricsOptions:AppTag"]);
    options.AddEnvTag(_configuration["MetricsOptions:EnvTag"]);
}).Report.ToInfluxDb(options =>
{
    options.InfluxDb.BaseUri = new Uri(_configuration["MetricsReportingInfluxDbOptions:InfluxDb:BaseUri"]);
    options.InfluxDb.Database = _configuration["MetricsReportingInfluxDbOptions:InfluxDb:Database"];

    //1.0认证 配置用户名密码,默认没有
    //options.InfluxDb.UserName = username;
    //options.InfluxDb.Password = password;

    options.InfluxDb.CreateDataBaseIfNotExists = bool.Parse(_configuration["MetricsReportingInfluxDbOptions:InfluxDb:CreateDataBaseIfNotExists"]);
    options.FlushInterval = TimeSpan.Parse(_configuration["MetricsReportingInfluxDbOptions:FlushInterval"]);
}).Build();

builder.Services.AddMetrics(metrics);
//添加报表
builder.Services.AddMetricsReportingHostedService();
builder.Services.AddMetricsTrackingMiddleware(_configuration);
builder.Services.AddMetricsEndpoints(_configuration);


var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseAuthorization();

app.MapControllers();


//UseMetrics
app.UseMetricsAllEndpoints();
app.UseMetricsAllMiddleware();


app.Run();

Grafana查看数据

InfluxDb 1.0添加数据源保存


添加面板,默认模板地址:https://grafana.com/dashboards/2125

根据envapp(和配置文件对应)查看面板

InfluxDb 2.0添加数据源保存,目前没有Grafana面板。


健康检查

健康检查可以参考这篇博客

下面我们就来讲解一下,如何使用App Metrics来实现我们的健康检查

效果如图:
App Metrics中的健康检查分为3种状态:

  1. 健康(绿),
  2. 亚健康(黄)
  3. 不健康(红)

健康检查有三个登记

  • Healthy 健康
  • Unhealthy 不良
  • Degraded 降级

编码实现

安装Neget包

App.Metrics.AspNetCore.Health
App.Metrics.Health.Checks.Http
App.Metrics.Health.Checks.Network
App.Metrics.Health.Checks.Process
App.Metrics.Health.Reporting.Metrics

在项目中新建一个HealthChecks文件夹,新建一个RedisHealthCheck

using App.Metrics.Health;

namespace WebApplication2
{
    public class RedisHealthCheck : HealthCheck
    {
        //面板名称;十秒检查一次
        public RedisHealthCheck() : base("Redis", TimeSpan.FromSeconds(10)) { }

        protected override ValueTask<HealthCheckResult> CheckAsync(CancellationToken cancellationToken = default)
        {
            try
            {
                if (true)
                {
                    //健康
                    return new ValueTask<HealthCheckResult>(HealthCheckResult.Healthy());
                }
                else
                {
                    //不健康
                    return new ValueTask<HealthCheckResult>(HealthCheckResult.Unhealthy());
                }
            }
            catch
            {
                return new ValueTask<HealthCheckResult>(HealthCheckResult.Unhealthy());
            }
        }
    }
}

Program.cs添加健康检查服务

builder.Services.AddHealth(SetupHealth(metrics));



static IHealthBuilder SetupHealth(IMetrics metrics)
{

    //var healthBuilder = serviceProvider.GetService<IHealthBuilder>();
    var healthBuilder = new HealthBuilder();
    var health = new HealthBuilder().Configuration
                              .Configure(p =>
                              {
                                  p.Enabled = true;
                                  p.ReportingEnabled = true;
                              })
                              .Report.ToMetrics(metrics)
                              //自定义Redis检查
                              .HealthChecks.AddCheck<RedisHealthCheck>()
                              //检测专用内存占用量是否超过阀值(2G)
                              .HealthChecks.AddProcessPrivateMemorySizeCheck("Private Memory Size", (2048L * 1024L) * 1024L)
                              //检测虚拟内存占用是否超过阀值(2G)
                              .HealthChecks.AddProcessVirtualMemorySizeCheck("Virtual Memory Size", (2048L * 1024L) * 1024L)
                              //检测占用内存是否超过2G
                              .HealthChecks.AddProcessPhysicalMemoryCheck("Working Set", (2048L * 1024L) * 1024L)
                              .HealthChecks.AddPingCheck("google ping", "google.com", TimeSpan.FromSeconds(10))
                              .HealthChecks.AddHttpGetCheck("github", new Uri("https://github.com/"), TimeSpan.FromSeconds(10))
                              .Build();


    var scheduler = new AppMetricsTaskScheduler(TimeSpan.FromSeconds(5), async () =>
                            {
                                //多线程发送报告
                                //await Task.WhenAll(metricsRoot.ReportRunner.RunAllAsync());

                                var healthStatus = await health.HealthCheckRunner.ReadAsync();

                                //using (var stream = new MemoryStream())
                                //{
                                //    await health.DefaultOutputHealthFormatter
                                //                .WriteAsync(stream, healthStatus);
                                //    var result = Encoding.UTF8.GetString(stream.ToArray());
                                //    Console.WriteLine(result);
                                //}
                                //发送报告
                                foreach (var reporter in health.Reporters)
                                    await reporter.ReportAsync(health.Options, healthStatus);
                            });
    scheduler.Start();
    return healthBuilder;
}

程序自身健康检查

using App.Metrics.Health;

namespace WebApplication.HealthChecks
{
    public class DelayStartupHealthCheck : HealthCheck
    {
        public DelayStartupHealthCheck()
           : base("DelayStartup", TimeSpan.FromSeconds(5)) { }

        protected override ValueTask<HealthCheckResult> CheckAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            if (DelayStartup.StartupReady)
            {
                return new ValueTask<HealthCheckResult>(HealthCheckResult.Healthy());
            }
            return new ValueTask<HealthCheckResult>(HealthCheckResult.Unhealthy());
        }
    }
   public class DelayStartup
    {
        public static bool StartupReady { get; private set; } = false;

        private static long DelaySeconds = 60;

        private static Semaphore semaphore = new Semaphore(0, 1);

        static DelayStartup()
        {
            try
            {
                //IConfiguration configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
                //DelaySeconds = int.Parse(configuration["App:DelayStartup"]);
                DelaySeconds = 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                DelaySeconds = 0;
            }
            if (DelaySeconds > 0)
            {
                Startup();
            }
            else
            {
                StartupReady = true;
            }
        }

        private static void Startup()
        {
            DateTime start = DateTime.Now;

            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    if (DateTime.Now.Subtract(start).TotalSeconds >= DelaySeconds)
                    {
                        StartupReady = true;
                        Console.WriteLine("DelayStartup StartupReady");
                        break;
                    }
                    Console.WriteLine("DelayStartup Checking");
                    semaphore.WaitOne(2000);
                }
            });
        }
    }
}

集成 Microsoft.Extensions.Diagnostics.HealthChecks

Microsoft.Extensions.Diagnostics.HealthChecks 使用可以参考这篇博客

Neget依赖还是上面哪那些包
新建健康检查类

using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Net.Http;

namespace WebApplication2.HealthChecks
{
    public class RemoteHealthCheck : IHealthCheck
    {
        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
        {
            using (var httpClient = new HttpClient())
            {
                var response = await httpClient.GetAsync("https://www.baidu.com");
                if (response.IsSuccessStatusCode)
                {
                    return HealthCheckResult.Healthy($"API is running.");
                }

                return HealthCheckResult.Unhealthy("API is not running");
            }
        }
    }

    public class DatabaseHealthCheck : IHealthCheck
    {
        public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken =
         default)
        {
            try
            {
                return Task.FromResult(HealthCheckResult.Healthy());

            }
            catch (Exception ex)
            {

                return Task.FromResult(HealthCheckResult.Unhealthy("DB is not Healthy", ex));
            }
        }
    }
}

Program.cs添加健康检查服务


//基于上面的代码添加配置

//30S轮询一次
builder.Services.AddHealthChecks()
    .AddCheck<RemoteHealthCheck>(nameof(RemoteHealthCheck))
    .AddCheck<DatabaseHealthCheck>(nameof(DatabaseHealthCheck)); 

builder.Services.AddAppMetricsHealthPublishing();


……

//app.UseHealthChecks("/health");
app.MapHealthChecks("/health");

运行程序然后访问 /health,会主动发起健康检查

查看面板

相关链接

https://www.cnblogs.com/yaozhenfa/p/12291749.html
https://www.cnblogs.com/edisonchou/p/integrated_performance_monitoring_foundation.html
posted @ 2022-11-16 01:01  雨水的命运  阅读(524)  评论(0编辑  收藏  举报