系统开发到一定的阶段,线上的机器越来越多,就需要一些监控了,除了服务器的监控,业务方面也需要一些监控服务。Metrics作为一款监控指标的度量类库,提供了许多工具帮助开发者来完成自定义的监控工作。

举个例子,一个图片压缩服务:

  • 每秒钟的请求数是多少(TPS)?
  • 平均每个请求处理的时间?
  • 请求处理的最长耗时?
  • 等待处理的请求队列长度?

又或者一个缓存服务:

  • 缓存的命中率?
  • 平均查询缓存的时间?

基本上每一个服务、应用都需要做一个监控系统,这需要尽量以少量的代码,实现统计某类数据的功能。

以Java为例,目前最为流行的metrics库是来自Coda Hale 的 dropwizard/metrics,该库被广泛地应用于各个知名的开源项目中。例如 Hadoop,Kafka,Spark,JStorm 中。

本文就结合范例来主要介绍下 dropwizard/metrics 的概念和用法。


Maven 配置

我们需要在pom.xml中依赖 metrics-core 包:

<dependencies>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>${metrics.version}</version>
</dependency>
</dependencies>

注:在POM文件中需要声明 ${metrics.version} 的具体版本号,如 3.1.0


Metric Registries

MetricRegistry类是Metrics的核心,它是存放应用中所有metrics的容器。也是我们使用 Metrics 库的起点。

MetricRegistry registry = new MetricRegistry();

每一个 metric 都有它独一无二的名字,Metrics 中使用句点名字,如 com.example.Queue.size。当你在 com.example.Queue 下有两个 metric 实例,可以指定地更具体:com.example.Queue.requests.size 和 com.example.Queue.response.size 。使用MetricRegistry类,可以非常方便地生成名字。

MetricRegistry.name(Queue.class, "requests", "size")
MetricRegistry.name(Queue.class, "responses", "size")

Metrics 数据展示

Metircs 提供了 Report 接口,用于展示 metrics 获取到的统计数据。metrics-core中主要实现了四种 reporter: JMX, console, SLF4J, 和 CSV。 在本文的例子中,我们使用 ConsoleReporter 。


五种 Metrics 类型

Gauges

最简单的度量指标,只有一个简单的返回值,例如,我们想衡量一个待处理队列中任务的个数,代码如下:

public class GaugeTest {

    public static Queue<String> q = new LinkedList<String>();

    public static void main(String[] args) throws InterruptedException {
MetricRegistry registry = new MetricRegistry();
ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
reporter.start(1, TimeUnit.SECONDS); registry.register(MetricRegistry.name(GaugeTest.class, "queue", "size"),
new Gauge<Integer>() { public Integer getValue() {
return q.size();
}
}); while(true){
Thread.sleep(1000);
q.add("Job-xxx");
}
}
}

运行之后的结果如下:

-- Gauges ------------------------------------------------
com.alibaba.wuchong.metrics.GaugeTest.queue.size
value = 6

其中第7行和第8行添加了ConsoleReporter,可以每秒钟将度量指标打印在屏幕上,理解起来会更清楚。

但是对于大多数队列数据结构,我们并不想简单地返回queue.size(),因为java.util和java.util.concurrent中实现的#size()方法很多都是 O(n) 的复杂度,这会影响 Gauge 的性能。

Counters

Counter 就是计数器,Counter 只是用 Gauge 封装了 AtomicLong 。我们可以使用如下的方法,使得获得队列大小更加高效。

public class CounterTest {

    public static Queue<String> q = new LinkedBlockingQueue<String>();

    public static Counter pendingJobs;

    public static Random random = new Random();

    public static void addJob(String job) {
pendingJobs.inc();
q.offer(job);
} public static String takeJob() {
pendingJobs.dec();
return q.poll();
} public static void main(String[] args) throws InterruptedException {
MetricRegistry registry = new MetricRegistry();
ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
reporter.start(1, TimeUnit.SECONDS); pendingJobs = registry.counter(MetricRegistry.name(Queue.class,"pending-jobs","size")); int num = 1;
while(true){
Thread.sleep(200);
if (random.nextDouble() > 0.7){
String job = takeJob();
System.out.println("take job : "+job);
}else{
String job = "Job-"+num;
addJob(job);
System.out.println("add job : "+job);
}
num++;
}
}
}

运行之后的结果大致如下:

add job : Job-15
add job : Job-16
take job : Job-8
take job : Job-10
add job : Job-19
15-8-1 16:11:31 ============================================
-- Counters ----------------------------------------------
java.util.Queue.pending-jobs.size
count = 5

Meters

Meter度量一系列事件发生的速率(rate),例如TPS。Meters会统计最近1分钟,5分钟,15分钟,还有全部时间的速率。

public class MeterTest {

    public static Random random = new Random();

    public static void request(Meter meter){
System.out.println("request");
meter.mark();
} public static void request(Meter meter, int n){
while(n > 0){
request(meter);
n--;
}
} public static void main(String[] args) throws InterruptedException {
MetricRegistry registry = new MetricRegistry();
ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
reporter.start(1, TimeUnit.SECONDS); Meter meterTps = registry.meter(MetricRegistry.name(MeterTest.class,"request","tps")); while(true){
request(meterTps,random.nextInt(5));
Thread.sleep(1000);
} }
}

运行结果大致如下:

request
15-8-1 16:23:25 ============================================ -- Meters ------------------------------------------------
com.alibaba.wuchong.metrics.MeterTest.request.tps
count = 134
mean rate = 2.13 events/second
1-minute rate = 2.52 events/second
5-minute rate = 3.16 events/second
15-minute rate = 3.32 events/second

注:非常像 Unix 系统中 uptime 和 top 中的 load。

Histograms

Histogram统计数据的分布情况。比如最小值,最大值,中间值,还有中位数,75百分位, 90百分位, 95百分位, 98百分位, 99百分位, 和 99.9百分位的值(percentiles)。

比如request的大小的分布:

public class HistogramTest {
public static Random random = new Random(); public static void main(String[] args) throws InterruptedException {
MetricRegistry registry = new MetricRegistry();
ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
reporter.start(1, TimeUnit.SECONDS); Histogram histogram = new Histogram(new ExponentiallyDecayingReservoir());
registry.register(MetricRegistry.name(HistogramTest.class, "request", "histogram"), histogram); while(true){
Thread.sleep(1000);
histogram.update(random.nextInt(100000));
} }
}

运行之后结果大致如下:

-- Histograms --------------------------------------------
java.util.Queue.queue.histogram
count = 56
min = 1122
max = 99650
mean = 48735.12
stddev = 28609.02
median = 49493.00
75% <= 72323.00
95% <= 90773.00
98% <= 94011.00
99% <= 99650.00
99.9% <= 99650.00

Timers

Timer其实是 Histogram 和 Meter 的结合, histogram 某部分代码/调用的耗时, meter统计TPS。

public class TimerTest {

    public static Random random = new Random();

    public static void main(String[] args) throws InterruptedException {
MetricRegistry registry = new MetricRegistry();
ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
reporter.start(1, TimeUnit.SECONDS); Timer timer = registry.timer(MetricRegistry.name(TimerTest.class,"get-latency")); Timer.Context ctx; while(true){
ctx = timer.time();
Thread.sleep(random.nextInt(1000));
ctx.stop();
} }
}

运行之后结果如下:

-- Timers ------------------------------------------------
com.alibaba.wuchong.metrics.TimerTest.get-latency
count = 38
mean rate = 1.90 calls/second
1-minute rate = 1.66 calls/second
5-minute rate = 1.61 calls/second
15-minute rate = 1.60 calls/second
min = 13.90 milliseconds
max = 988.71 milliseconds
mean = 519.21 milliseconds
stddev = 286.23 milliseconds
median = 553.84 milliseconds
75% <= 763.64 milliseconds
95% <= 943.27 milliseconds
98% <= 988.71 milliseconds
99% <= 988.71 milliseconds
99.9% <= 988.71 milliseconds

其他

初次之外,Metrics还提供了HealthCheck 用来检测某个某个系统是否健康,例如数据库连接是否正常。还有Metrics Annotation,可以很方便地实现统计某个方法,某个值的数据。感兴趣的可以点进链接看看。


使用经验总结

一般情况下,当我们需要统计某个函数被调用的频率(TPS),会使用Meters。当我们需要统计某个函数的执行耗时时,会使用Histograms。当我们既要统计TPS又要统计耗时时,我们会使用Timers。


参考资料

Metrics Core

[Metrics Getting Started](Metrics Getting Started)

Metrics的更多相关文章

  1. 使用Metrics.NET 构建 ASP.NET MVC 应用程序的性能指标

    通常我们需要监测ASP.NET MVC 或 Web API 的应用程序的性能时,通常采用的是自定义性能计数器,性能计数器会引发无休止的运维问题(损坏的计数器.权限问题等).这篇文章向你介绍一个新的替代 ...

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

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

  3. 三维网格精简算法(Quadric Error Metrics)附源码

    在计算机图形应用中,为了尽可能真实呈现虚拟物体,往往需要高精度的三维模型.然而,模型的复杂性直接关系到它的计算成本,因此高精度的模型在几何运算时并不是必须的,取而代之的是一个相对简化的三维模型,那么如 ...

  4. Metrics.NET 项目

    Metrics.NET(https://github.com/etishor/Metrics.NET)是一个给CLR 提供度量工具的包,它是移植自Java的metrics,在c#代码中嵌入Metric ...

  5. Jmeter plugin jp@gc - PerfMon Metrics Collector

    Jmeter由于是开源工具,所以目前有很多插件可以供使用,最简单的方法是先把Plugin Manager安装了 下载地址:https://jmeter-plugins.org/wiki/Plugins ...

  6. 【原创】Kakfa metrics包源代码分析

    这个包主要是与Kafka度量相关的. 一.KafkaTimer.scala 对代码块的运行进行计时.仅提供一个方法: timer——在运行传入函数f的同时为期计时 二.KafkaMetricsConf ...

  7. Metrics介绍

    Metrics可以为你的代码的运行提供无与伦比的洞察力.作为一款监控指标的度量类库,它提供了很多模块可以为第三方库或者应用提供辅助统计信息, 比如Jetty, Logback, Log4j, Apac ...

  8. Flink - metrics

      Metrics是以MetricsGroup来组织的 MetricGroup MetricGroup 这就是个metric容器,里面可以放subGroup,或者各种metric 所以主要的接口就是注 ...

  9. 第三十六章 metrics(4)- metrics-graphite

    将metrics report给graphite(carbon-relay) 一.代码 1.pom.xml <!-- metrics-graphite --> <dependency ...

  10. 第三十五章 metrics(3)- codahale-metrics基本使用

    <!-- metrics --> <dependency> <groupId>io.dropwizard.metrics</groupId> <a ...

随机推荐

  1. ASP.NET Core MVC和Visual Studio入门

    本教程将教你使用Visual Studio 2017创建 ASP.NET Core MVC web应用程序的基础知识. 安装Visual Studio 2017 和.Net Core 安装Visual ...

  2. kindeditor修改图片上传路径-使用webapi上传图片到图片服务器

    kindeditor是一个非常好用的富文本编辑器,它的简单使用我就不再介绍了. 在这里我着重介绍一些使用kindeditor修改图片上传路径并通过webapi上传图片到图片服务器的方案. 因为我使用的 ...

  3. JQgrid表格的使用

    html部分: <div class="tab"> <table id="datatable"></table>      ...

  4. es6基础系列一:let和const

    let 声明变量,可以说是具有作用域的var,用于声明变量,主要规则如下: 1 let声明的变量拥有块级作用域 { let i = 1; console.log(i); // 1 } console. ...

  5. [Python]再学 socket 之非阻塞 Server

    再学 socket 之非阻塞 Server 本文是基于 python2.7 实现,运行于 Mac 系统下 本篇文章是上一篇初探 socket 的续集, 上一篇文章介绍了:如何建立起一个基本的 sock ...

  6. 烧录口被初始化为普通IO

    烧录口被初始化为普通IO后如果复位端没有的烧录口会导致不能识别烧录器不能下载与调试,因为程序一开始就把端口初始化了,烧录器不能识别,添加复位端口到烧录器(前提是你的烧录器有复位端). 有了复位段之后, ...

  7. MySQL 事务与锁机制

    下表展示了本人安装的MariaDB(10.1.19,MySQL的分支)所支持的所有存储引擎概况,其中支持事务的有InnoDB.SEQUENCE,另外InnoDB还支持XA事务,MyISAM不支持事务. ...

  8. java设计模式--基础思想总结--父类引用操作对象

    看设计模式的相关书籍也有一段时间了,一开始其实是抱着作为java三大框架的基础知识储备来学习的,不过到后来,才发现,在设计模式的一些准则装饰下,java的面向对象威力才真正地体现出来,后面的将会陆续地 ...

  9. 最新的css3动画按钮效果

    效果演示     插件下载

  10. SQL Server 数据库连接方法

    我们用c#写ado或者是asp,都需要连接数据库来读写数据,今天我们就来总结一下数据库连接都有哪些方法. 首先我们就写最直接的方法,在事件中直接连接.(在这里就用WEB页面来展示) 首先我们建立web ...