使用Metrics监控应用程序的性能

在编写应用程序的时候,通常会记录日志以便事后分析,在很多情况下是产生了问题之后,再去查看日志,是一种事后的静态分析。在很多时候,我们可能需要了解整个系统在当前,或者某一时刻运行的情况,比如当前系统中对外提供了多少次服务,这些服务的响应时间是多少,随时间变化的情况是什么样的,系统出错的频率是多少。这些动态的准实时信息对于监控整个系统的运行健康状况来说很重要。

一些应用程序,比如对外提供接口或者服务的WebService,对整个系统的实时运行情况进行监控显得尤为重要,着就像我们操作系统里面的资源管理器一样,如果能够实时或者准实时的看到整个系统耗费的CPU,内存等资源,对我们快速对系统做出响应,以及优化很重要。并且,这些实时的性能参数信息,对于一些高级应用场景,比如服务的熔断机制(需要实时统计系统出错比例和响应时间),只有做到了实时监控才能提供这些数据,才能实现这种提高系统稳健性的功能。

前几天在InfoQ的一篇采访文章中看到一句话 “Profiling特别重要。如果能有一个特别强大的Profiling系统,就知道整个系统在哪个地方,哪台机器上,花了多少CPU、内存、磁盘IO或者网络带宽等资源,才能知道优化什么地方效益最大。”

同样,对于WebService的监控,比如在那个地方,那台机器上,花了多少CPU,多少内存,每一个服务的响应时间,出错的次数频率等,这些信息记录下来之后,我们就可以看到服务在运行时的动态的表现,更加容易找出错误或者定位问题点来进行优化。

最简单的做法是,在应用系统的关键地方,或者所有程序的入口,出口进行埋点,然后将这些采样信息不断的发送到某一个消息队列或者内存DB中,然后其他系统进行读取分析和展示。

在Java中有一个开源的名为Metrics的项目,它能够捕获JVM以及应用层面的性能参数,他的作者Coda Hale介绍了什么是Mertics并且为什么Metrics在应用程序系统中很有必要,视频和演讲可以分别到YouTubeSlideShare上可以下载。在.NET中有对其进行移植的项目叫 Metrics.NET

在开始介绍Metric之前,需要先看看一些基本的度量器类型,以及常见的使用场景,才能知道如何使用。

一 度量类型

Metrics提供5种基本的度量类型:Gauges, Counters, Histograms, Meters和 Timers

Gauge

Gauge是最简单的度量类型,只有一个简单的返回值,他用来记录一些对象或者事物的瞬时值。

比如,我们类型为Gauge的计数器来记录某个服务目前开通的城市个数

  1. Metric.Gauge("Service Cities Count", () => Cities.Count, new Unit("个"));

Counters

Counter是一个简单64位的计数器,他可以增加和减少。

比如我们可以定义两个Counter类型的计数器,用来统计所有服务请求数目,和目前正在处理的请求总数。

  1. /// <summary>
  2. /// keep the total count of the requests
  3. /// </summary>
  4. private readonly Counter totalRequestsCounter = Metric.Counter("Requests", Unit.Requests);
  5.  
  6. /// <summary>
  7. /// count the current concurrent requests
  8. /// </summary>
  9. private readonly Counter concurrentRequestsCounter = Metric.Counter("SampleMetrics.ConcurrentRequests", Unit.Requests);

这样,在我们请求处理开始的时候,同时将这两个计数器自增。

  1. this.concurrentRequestsCounter.Increment(); // increment concurrent requests counter
  2. this.totalRequestsCounter.Increment(); // increment total requests counter

当某一个请求处理完成之后,将目前正在处理的请求减一

  1. this.concurrentRequestsCounter.Decrement(); // decrement number of concurrent requests

这种计数器也可以用来统计诸如当前有多少人在线,或者服务器中有多少处于有效期内的session

Meters

Meter是一种只能自增的计数器,通常用来度量一系列事件发生的比率。他提供了平均速率,以及指数平滑平均速率,以及采样后的1分钟,5分钟,15分钟速率。

比如需要统计请求的速率,比如统计平均每分钟内有多少次请求进来。只需要定义一个metric

  1. /// <summary>
  2. /// measure the rate at which requests come in
  3. /// </summary>
  4. private readonly Meter meter = Metric.Meter("Requests", Unit.Requests,TimeUnit.Seconds);

在处理请求的地方,调用Mark方法即可。

  1. this.meter.Mark(); // signal a new request to the meter

再比如,要测量服务出错的概率,比如每小时出错多少次。可以定义一个metric。

  1. /// <summary>
  2. /// measure the rate of service exception
  3. /// </summary>
  4. private readonly Meter errorMeter = Metric.Meter("Error", Unit.Errors, TimeUnit.Hours);

这样,在处理请求的时候,如果出现异常了,调用一下errorMeter的Mark方法即可。

  1. this.errorMeter.Mark();// signal a new error to the meter

Histograms

Histrogram是用来度量流数据中Value的分布情况,Histrogram可以计算最大/小值、平均值,方差,分位数(如中位数,或者95th分位数),如75%,90%,98%,99%的数据在哪个范围内。

比如,我们想度量,所有传进来服务的请求参数的长度分布。那么,可以定义一个histogram。

  1. /// <summary>
  2. /// keep a histogram of the input data of our request method
  3. /// </summary>
  4. private readonly Histogram histogramOfData = Metric.Histogram("ResultsExample", Unit.Items);

然后在请求的地方,调用其Update方法来更新值。

  1. this.histogramOfData.Update(request.length, methodName); // update the histogram with the input data

Timer

Timer是Histogram跟Meter的一个组合,比如要统计当前请求的速率和处理时间。

就可以定义一个Timer:

  1. /// <summary>
  2. /// measure the time rate and duration of requests
  3. /// </summary>
  4. private readonly Timer timer = Metric.Timer("Requests", Unit.Requests);

在使用的时候,调用timer的NewContext即可。

  1. using (this.timer.NewContext(i.ToString())) // measure until disposed
  2. {
  3. ...
  4. }

二 度量数据的输出

收集了这么多数据之后,我们需要把数据时实的动态展示或者保存起来。Metric提供了多种的数据报告接口。包括自带的Metrics.NET.FlotVisualization, 以及输出到专业的系统监控Graphite,输出到开源,分布式,时间序列的中InfluxDB,或者输出到ElasticSearch中。配置起来也非常简单。比如如果要直接在http页面上展现,只需要在初始化的时候,设置合适的EndPoint即可:

  1. Metric.Config
  2. .WithHttpEndpoint("http://localhost:1234/metrics/")
  3. .WithAllCounters()
  4. .WithInternalMetrics()
  5. .WithReporting(config => config
  6. .WithConsoleReport(TimeSpan.FromSeconds(30))

然后在浏览器中输入 http://localhost:1234/metrics/,就可以看到各种采集的准实时各种度量信息:

上面自带的性能DashBoard略显简陋。 通常,我们一般会将这些实时采集的数据存储到分布式时序数据库InfluxDB中,然后利用开源的图表控件Grafana来实时展现这些数据,比如,可以制作想下面这样的,动态的性能准实时监控系统:

三 总结

本文介绍了如何使用埋点和各种度量工具来实时监测应用程序的性能,介绍了.NET中Metrics度量工具的使用。与传统的记录日志的方式不同,这种实时或者准实时的对当前系统各种关键指标的采样和监控,对于应用程序的运维,性能优化,提供了一种动态的视角,能帮助我们更好的了解当前应用程序或者服务在线上的各种性能参数和表现状态。Metrics的采样应该尽量减少对原有系统的侵入性,所以一般的最好是将采样的结果存储到消息队列或者内存DB中,然后进行动态展示,另外采样频率也是需要考虑的一个重要因素。因为对于一个较大的系统来说,实时采样产生的数据量会比较大。InfluxDB 似乎只能在非Windows平台使用,所以本文没有完整演示整个Metrics+InfluxDB+Grafana 构建应用程序实时监控系统的搭建。不过相信大家对照着相关文档,应该不难实现。

希望本文对您了解如何实时监控应用程序性能,以及如何构建应用程序性能参数dashboard有所帮助。

四 参考资料

Metrics.NET step by step

安装Nuget包

nuget中搜索metrics,如图:

配置Metrics

在程序入口处插入配置Metrics的代码。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Metric.Config
  6. // Web监视仪表板,提供Metrics.NET度量值图表,浏览器打开这个地址可以访问一个Metrics.NET内置的页面
  7. .WithHttpEndpoint("http://localhost:1234/metrics/")
  8. // 配置报表输出
  9. .WithReporting((rc)=>{
  10. // 报表输出到控制台
  11. rc.WithConsoleReport(TimeSpan.FromSeconds(5));
  12. });
  13. Console.ReadLine();
  14. }
  15. }

这个时候打开http://localhost:1234/metrics/将会看到一个空白的页面,里面啥也没有,各个度量菜单中也没有任何度量项。

收集度量值

Main入口中添加度量项,后面需要分别实现这些度量项。

  1. static void Main(string[] args)
  2. {
  3. Metric.Config
  4. // Web监视仪表板,提供Metrics.NET度量值图表
  5. .WithHttpEndpoint("http://localhost:1234/metrics/")
  6. // 配置报表输出
  7. .WithReporting((rc) =>
  8. {
  9. // 报表输出到控制台
  10. rc.WithConsoleReport(TimeSpan.FromSeconds(5));
  11. });
  12.  
  13. GaugeTest();
  14. CounterTest();
  15. HistogramTest();
  16. MeterTest();
  17. TimerTest();
  18. HealthCheckTest();
  19.  
  20. Console.ReadLine();
  21. }

Gauge

  1. static void GaugeTest()
  2. {
  3. Metric.Gauge("test.gauge", () => ran.NextDouble() * 1000, Unit.None);
  4. }

最简单的度量,Metrics不做任何统计计算,读取一个即时值。

Counter

  1. static Random ran = new Random(DateTime.Now.TimeOfDay.Milliseconds);
  2. static void CounterTest()
  3. {
  4. var counter = Metric.Counter("test.counter", Unit.Custom("并发"));
  5.  
  6. Action doWork = () => { System.Threading.Thread.Sleep(ran.Next(10, 300)); };
  7. Action idlesse = () => { System.Threading.Thread.Sleep(ran.Next(0, 500)); };
  8. for (var i = 0; i < 20; i++)
  9. {
  10. Task.Run(() =>
  11. {
  12. while (true)
  13. {
  14. counter.Increment();
  15. doWork();
  16. counter.Decrement();
  17. idlesse();
  18. }
  19. });
  20. }
  21. }

示例代码展示如何度量并发数。

Histogram

  1. static Random ran = new Random(DateTime.Now.TimeOfDay.Milliseconds);
  2. static void HistogramTest()
  3. {
  4. var histogram = Metric.Histogram("test.histogram", Unit.Custom("岁"), SamplingType.LongTerm);
  5.  
  6. Task.Run(() =>
  7. {
  8. while (true)
  9. {
  10. histogram.Update(ran.Next(10, 80), ran.Next(0, 2) > 0 ? "男" : "女");
  11. System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));
  12. }
  13. });
  14. }

示例代码展示每1秒钟录入一个人的年龄,使用Histogram统计所有录入的人的年龄的最新值,平均值,最大最小值,分位数(75%,95%,98%,99%,99.9%)等。从效果上看,可以认为Histogram是Gauge的复杂(数值统计)版。

Meter

  1. static Random ran = new Random(DateTime.Now.TimeOfDay.Milliseconds);
  2. static void MeterTest()
  3. {
  4. var meter = Metric.Meter("test.meter", Unit.Calls, TimeUnit.Seconds);
  5.  
  6. Action idlesse = () => { System.Threading.Thread.Sleep(ran.Next(20, 50)); };
  7. Task.Run(() => {
  8. while(true)
  9. {
  10. meter.Mark();
  11. idlesse();
  12. }
  13. });
  14. }

示例代码展示了如何统计某个事件每秒钟发生的次数,可以用来统计qps或tps,除了全部数据的qps,和tps,还提供了最近1分钟、5分钟、15分钟的事件频率。调用里有个时间单位(TimeUnit)参数rateUnit,频率=总次数/总时间,这个rateUnit就是总时间的单位。

Timer

  1. static Random ran = new Random(DateTime.Now.TimeOfDay.Milliseconds);
  2. static void TimerTest()
  3. {
  4. var timer = Metric.Timer("test.meter", Unit.None, SamplingType.FavourRecent, TimeUnit.Seconds, TimeUnit.Microseconds);
  5.  
  6. Action doWork = () => { System.Threading.Thread.Sleep(ran.Next(10, 300)); };
  7. Action idlesse = () => { System.Threading.Thread.Sleep(ran.Next(0, 500)); };
  8. for (var i = 0; i < 20; i++)
  9. {
  10. Task.Run(() =>
  11. {
  12. while (true)
  13. {
  14. timer.Time(doWork);
  15. idlesse();
  16. }
  17. });
  18. }
  19. }

timer是meter和histogram结合,在meter的基础上,统计了每个业务处理的耗时的histogram度量信息,同时记录并发数。创建timer需要两个时间单位(TimeUnit),第一个rateUnit同meter里的含义一样,第二个durationUnit是统计每个业务处理耗时的时间单位。

HealthCheck

  1. static Random ran = new Random(DateTime.Now.TimeOfDay.Milliseconds);
  2. static void HealthCheckTest()
  3. {
  4. HealthChecks.RegisterHealthCheck("test.healthcheck", () =>
  5. {
  6. return ran.Next(100) < 5 ? HealthCheckResult.Unhealthy() : HealthCheckResult.Healthy();
  7. });
  8. }

健康状况检查很简单,输出就两个值:健康(healthy)、不健康(unhealthy)。

度量值类型

非严谨关系概览

GaugeValue : double

CounterValue

  1. {
  2. "Count" : "long"
  3. "Items" : [{
  4. "Item" : "string",
  5. "Count" : "long",
  6. "Percent" : "double"
  7. }]
  8. }

HistogramValue

  1. {
  2. "Count" : "long",
  3. "LastValue" : "double",
  4. "LastUserValue" : "string",
  5. "Max" : "double",
  6. "MaxUserValue" : "string",
  7. "Mean" : "double",
  8. "Min" : "double";
  9. "MinUserValue" : "string",
  10. "StdDev" : "double",
  11. "Median" : "double",
  12. "Percentile75" : "double",
  13. "Percentile95" : "double",
  14. "Percentile98" : "double",
  15. "Percentile99" : "double",
  16. "Percentile999" : "double",
  17. "SampleSize" : "int"
  18. }

MeterValue

  1. {
  2. "Count" : "long",
  3. "MeanRate" : "double",
  4. "OneMinuteRate" : "double",
  5. "FiveMinuteRate" : "double",
  6. "FifteenMinuteRate" : "double",
  7. "RateUnit" : "TimeUnit",
  8. "Items" : [{
  9. "Item" : "string",
  10. "Percent" : "double",
  11. "Value" : {
  12. "Count" : "long",
  13. "MeanRate" : "double",
  14. "OneMinuteRate" : "double",
  15. "FiveMinuteRate" : "double",
  16. "FifteenMinuteRate" : "double",
  17. "RateUnit" : "TimeUnit"
  18. }
  19. }]
  20. }

TimerValue

  1. {
  2. "Rate" : "MeterValue",
  3. "Histogram" : "HistogramValue",
  4. "ActiveSessions" : "long"
  5. }

度量值采集方式

Gauge

使用时读取,无需额外函数支持

Counter

Increase(long value=1, string item=null) : void //增加计数

Decrease(long value=1, string item=null) : void //减少计数

Histogram

Update(double value, string userValue=null) : void

Meter

Mark(long count=1, string item=null) : void

Timer

Record(long duration, TimeUnit unit,  string userValue=null) : void

Timer(Action action, string userValue=null) : void

Timer<T>(Func<T> action, string userValue=null) : T

下载

MetricsDemo.rar

作者:binking338 blog:http://www.cnblogs.com/binking338

https://www.cnblogs.com/binking338/p/4829123.html

Metrics.NET step by step使用Metrics监控应用程序的性能的更多相关文章

  1. 使用Metrics监控应用程序的性能

    在编写应用程序的时候,通常会记录日志以便事后分析,在很多情况下是产生了问题之后,再去查看日志,是一种事后的静态分析.在很多时候,我们可能需要了解整个系统在当前,或者某一时刻运行的情况,比如当前系统中对 ...

  2. timeSeries db之:使用Metrics监控应用程序的性能 (zz)

    在编写应用程序的时候,通常会记录日志以便事后分析,在很多情况下是产生了问题之后,再去查看日志,是一种事后的静态分析.在很多时候,我们可能需要了解整个系统在当前,或者某一时刻运行的情况,比如当前系统中对 ...

  3. Metrics.NET step by step

    安装Nuget包 nuget中搜索metrics,如图: 配置Metrics 在程序入口处插入配置Metrics的代码. class Program { static void Main(string ...

  4. 使用 Metrics.net + influxdb + grafana 搭建项目自动化监控和预警方案

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qq_25042791/article/d ...

  5. Linkerd 2.10(Step by Step)—2. 自动化的金丝雀发布

    通过结合 Linkerd 和 Flagger 来根据服务指标自动金丝雀(canary)发布,从而降低部署风险. Linkerd 2.10 中文手册持续修正更新中: https://linkerd.ha ...

  6. Step by Step Process of Migrating non-CDBs and PDBs Using ASM for File Storage (Doc ID 1576755.1)

    Step by Step Process of Migrating non-CDBs and PDBs Using ASM for File Storage (Doc ID 1576755.1) AP ...

  7. EF框架step by step(7)—Code First DataAnnotations(1)

    Data annotation特性是在.NET 3.5中引进的,给ASP.NET web应用中的类提供了一种添加验证的方式.Code First允许你使用代码来建立实体框架模型,同时允许用Data a ...

  8. Step By Step(Lua元表与元方法)

    Step By Step(Lua元表与元方法) Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表 ...

  9. Linkerd 2.10(Step by Step)—配置超时

    Linkerd 2.10 系列 快速上手 Linkerd v2 Service Mesh(服务网格) 腾讯云 K8S 集群实战 Service Mesh-Linkerd2 & Traefik2 ...

随机推荐

  1. Django之Template

    模板层(template) 概念:  模板与html的区别:  模板=html+模板语法 模板语法: 1 变量:       {{}}    深度查询: 通过句点符.    列表,字典    clas ...

  2. [题目] Luogu P3707 [SDOI2017]相关分析

    参考资料:[Luogu 3707] SDOI2017 相关分析 P3707 [SDOI2017]相关分析 TFRAC FRAC DFRAC \(\tfrac{\sum}{1}\) \(\frac{\s ...

  3. 配置Linux下vim自动缩进等功能

    从终端打开配置文件: vim ~/.vimrc 添加如下代码: set tabstop=4 set softtabstop=4 set shiftwidth=4 set autoindent set ...

  4. PySpider HTTP 599: SSL certificate problem错误的解决方法

    在用 PySpider 爬取 https 开头的网站的时候遇到了 HTTP 599: SSL certificate problem: self signed certificate in certi ...

  5. spring+springmvc+hibernate整合实例

    最近要弄一个自动化生成表及其实体对应的增删改查的框架,于是我想到了hibernate,hibernate就有根据实体自动建表,而且增删改查,都不需要想mybatis那样在xml文件中配置. 不过怎样让 ...

  6. SSM框架之整合(Maven实例)

    有不少朋友在maven中因为pom文件依赖的事导致报错 今天我这个快速搭建ssm框架,确保在jdk7或者jdk8的环境,tomcat没什么要求.但如果要用jdk8的话,最好用run as中的serve ...

  7. junit常用注解详细说明

    Java注解((Annotation)的使用方法是@注解名 ,能通过简单的词语来实现一些功能.在junit中常用的注解有@Test.@Ignore.@BeforeClass.@AfterClass.@ ...

  8. 深入浅出的webpack构建工具---ParallelUglifyPlugin优化压缩(十)

    webpack默认提供了UglifyJS插件来压缩JS代码,但是它使用的是单线程压缩代码,也就是说多个js文件需要被压缩,它需要一个个文件进行压缩.所以说在正式环境打包压缩代码速度非常慢(因为压缩JS ...

  9. MySQL(一)MySQL基础介绍

    最近的学习内容是数据库相关的一些知识,主要以MySQL为主,参考书籍——<MySQL必知必会> MySQL学习及下载地址:https://dev.mysql.com/ MySQL学习使用注 ...

  10. elasticsearch6.1 安装问题

    问题:Caused by: java.lang.RuntimeException: can not run elasticsearch as root [root@localhost logs]# a ...