概念

Metrics是一个给JAVA服务的各项指标提供度量工具的包,在JAVA代码中嵌入Metrics代码,可以方便的对业务代码的各个指标进行监控

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

有一些优点:

  • 提供了对Ehcache、Apache HttpClient、JDBI、Jersey、Jetty、Log4J、Logback、JVM等的集成
  • 支持多种Metric指标:Gauges、Counters、Meters、Histograms和Timers
  • 支持多种Reporter发布指标
    • JMX、Console,CSV文件和SLF4J loggers
    • Ganglia、Graphite,用于图形化展示

MetricRegistry

MetricRegistry类是Metrics的核心,它是存放应用中所有metrics的容器。也是我们使用 Metrics 库的起点。其中maven依赖添加在文末。

1
static final MetricRegistry metrics = new MetricRegistry();

Reporter

指标获取之后需要上传到各种地方,就需要用到Reporter。

控制台

监控指标直接打印在控制台

1
2
3
4
5
6
7
pravite static void startReportConsole() {
ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(1, TimeUnit.SECONDS);
}

JMX

将监控指标上报到JMX中,后续可以通过其他的开源工具上传到Graphite等供图形化展示。从Jconsole中MBean中能看到。

1
2
3
4
pravite static void startReportJmx(){
JmxReporter reporterJmx = JmxReporter.forRegistry(metrics).build();
reporterJmx.start();
}

Graphite

将监控指标上传到Graphite,从Graphite-web中能看到上传的监控指标。

1
2
3
4
5
6
7
8
9
10
pravite static void startReportGraphite(){
Graphite graphite = new Graphite(new InetSocketAddress("graphite.xxx.com", 2003));
GraphiteReporter reporter = GraphiteReporter.forRegistry(metrics)
.prefixedWith("test.metrics")
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.filter(MetricFilter.ALL)
.build(graphite);
reporter.start(1, TimeUnit.MINUTES);
}

封装各种Reporter

调用方式MetricCommon.getMetricAndStartReport();

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MetricCommon {
private static final MetricRegistry metricRegistry = new MetricRegistry();
public static MetricRegistry getMetricAndStartReport(){
startReportConsole();
startReportJmx();
startReportGraphite();
return metricRegistry;
}
pravite static void startReportConsole() {...}
pravite static void startReportJmx(){...}
pravite static void startReportGraphite(){...}
}

Metics指标

Metrics 有如下监控指标:

  • Gauges:记录一个瞬时值。例如一个待处理队列的长度。
  • Histograms:统计单个数据的分布情况,最大值、最小值、平均值、中位数,百分比(75%、90%、95%、98%、99%和99.9%)
  • Meters:统计调用的频率(TPS),总的请求数,平均每秒的请求数,以及最近的1、5、15分钟的平均TPS
  • Timers:当我们既要统计TPS又要统计耗时分布情况,Timer基于Histograms和Meters来实现
  • Counter:计数器,自带inc()和dec()方法计数,初始为0。
  • Health Checks:用于对Application、其子模块或者关联模块的运行是否正常做检测

Gauges

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class GaugeTest {
private static final MetricRegistry registry = MetricCommon.getMetricAndStartReport();
private static final Random random = new Random();
@Test
public void testOneGuage() throws InterruptedException {
Queue queue= new LinkedList<String>();
registry.register(MetricRegistry.name(GaugeTest.class, "testGauges-queue-size", "size"),
(Gauge<Integer>) () -> queue.size());
while(true){
Thread.sleep(1000);
queue.add("Job-xxx");
}
}
@Test
public void testMultiGuage() throws InterruptedException {
Map<Integer, Integer> map = new ConcurrentHashMap<>();
while(true){
int i = random.nextInt(100);
int j = i % 10;
if(!map.containsKey(j)){
map.put(j,i);
registry.register(MetricRegistry.name(GaugeTest.class, "testGauges-number", String.valueOf(j)),
(Gauge<Integer>) () -> map.get(j));
}else{
map.put(j,i);
}
Thread.sleep(1000);
}
}
}

第一个测试用例,是用一个guage记录队列的长度

1
2
3
-- Gauges ----------------------------------------------------------------------
GaugeTest.testGauges-queue-size.size
value = 4

第二个测试用例,每次产生一个100以内的随机数,将这些数以个位数的数字分组,guage记录每一组现在是什么数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- Gauges ----------------------------------------------------------------------
GaugeTest.testGauges-number.0
value = 60
GaugeTest.testGauges-number.1
value = 1
GaugeTest.testGauges-number.2
value = 82
GaugeTest.testGauges-number.3
value = 23
GaugeTest.testGauges-number.4
value = 74
GaugeTest.testGauges-number.5
value = 25
GaugeTest.testGauges-number.7
value = 17
GaugeTest.testGauges-number.8
value = 78
GaugeTest.testGauges-number.9
value = 69

Histogram

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HistogramTest {
private static final MetricRegistry registry = MetricCommon.getMetricAndStartReport();
public static Random random = new Random();
@Test
public void test() throws InterruptedException {
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));
}
}
}

运行很长时间之后,相当于随机值取极限,会趋向于统计值,75%肯定是要<=75000,99.9%肯定是要<=999000。

1
2
3
4
5
6
7
8
9
10
11
12
13
-- Histograms ------------------------------------------------------------------
HistogramTest.request.histogram
count = 1336
min = 97
max = 99930
mean = 49816.49
stddev = 29435.27
median = 49368.00
75% <= 75803.00
95% <= 95340.00
98% <= 98096.00
99% <= 98724.00
99.9% <= 99930.00

Meters

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MetersTest {
MetricRegistry registry = MetricCommon.getMetricAndStartAllReport("nc110x.corp.youdao.com","test.metrics");
public static Random random = new Random();
@Test
public void testOne() throws InterruptedException {
Meter meterTps = registry.meter(MetricRegistry.name(MetersTest.class,"request","tps"));
while(true){
meterTps.mark();
Thread.sleep(random.nextInt(1000));
}
}
@Test
public void testMulti() throws InterruptedException {
while(true){
int i = random.nextInt(100);
int j = i % 10;
Meter meterTps = registry.meter(MetricRegistry.name(MetersTest.class,"request","tps",String.valueOf(j)));
meterTps.mark();
Thread.sleep(10);
}
}
}

这里,多个注册多个meter与注册多个guage、Histograms用法会有不同,meter方法是getOrAdd

1
2
3
public Meter meter(String name) {
return (Meter)this.getOrAdd(name, MetricRegistry.MetricBuilder.METERS);
}

一个meter的测试用例,运行结果如下。可以看到随着次数的增多,各种rate无限趋近于2次。

1
2
3
4
5
6
7
-- Meters ------------------------------- 大专栏  Java程序监控---Metrics---------------------------------------
MetersTest.request.tps
count = 452
mean rate = 1.99 events/second
1-minute rate = 2.03 events/second
5-minute rate = 2.00 events/second
15-minute rate = 2.00 events/second

多个meter的测试用例,运行结果取了数字个位数为6/7/8的三个如下。最后都会无限趋近于10。sleep时间为10ms,每秒有100份,平均到尾数不同的,每组就有10份。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MetersTest.request.tps.6
count = 905
mean rate = 9.74 events/second
1-minute rate = 9.76 events/second
5-minute rate = 9.94 events/second
15-minute rate = 9.98 events/second
MetersTest.request.tps.7
count = 935
mean rate = 10.07 events/second
1-minute rate = 10.62 events/second
5-minute rate = 11.82 events/second
15-minute rate = 12.19 events/second
MetersTest.request.tps.8
count = 937
mean rate = 10.09 events/second
1-minute rate = 10.09 events/second
5-minute rate = 10.31 events/second
15-minute rate = 10.37 events/second

Timer

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class TimerTest {
public static Random random = new Random();
private static final MetricRegistry registry = MetricCommon.getMetricAndStartAllReport("nc110x.corp.youdao.com","test.metrics");
private static final Map<Integer,Timer> timerMap = new ConcurrentHashMap<>();
@Test
public void testOneTimer() throws InterruptedException {
Timer timer = registry.timer(MetricRegistry.name(TestTimer.class,"get-latency"));
Timer.Context ctx;
while(true){
ctx = timer.time();
Thread.sleep(random.nextInt(1000));
ctx.stop();
}
}
@Test
public void testMultiTimer() throws InterruptedException {
while(true){
int i = random.nextInt(100);
int j = i % 10;
Timer timer = registry.timer(MetricRegistry.name(TestTimer.class,"get-latency",String.valueOf(j)));
Timer.Context ctx;
ctx = timer.time();
Thread.sleep(random.nextInt(1000));
ctx.stop();
Thread.sleep(1000);
}
}
}

测试用例1是单个timer,结果如下。最后的时间都趋近于统计值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- Timers ----------------------------------------------------------------------
com.testmetrics.TestTimer.get-latency
count = 657
mean rate = 2.05 calls/second
1-minute rate = 1.98 calls/second
5-minute rate = 2.02 calls/second
15-minute rate = 2.01 calls/second
min = 4.98 milliseconds
max = 998.93 milliseconds
mean = 496.79 milliseconds
stddev = 297.46 milliseconds
median = 501.02 milliseconds
75% <= 765.09 milliseconds
95% <= 952.03 milliseconds
98% <= 974.12 milliseconds
99% <= 989.02 milliseconds
99.9% <= 998.93 milliseconds

Counters

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class CounterTest {
private static final MetricRegistry registry = MetricCommon.getMetricAndStartReport();
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();
}
@Test
public void test() throws InterruptedException {
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++;
}
}
}

job会越来越多,因为每次取走只取一个job,但是加入job是加入num个,num会一直增加,而概率是7:3。

1
2
3
-- Counters --------------------------------------------------------------------
java.util.Queue.pending-jobs.size
count = 36

HeathChecks

Metrics提供了一个独立的模块:Health Checks,用于对Application、其子模块或者关联模块的运行是否正常做检测。该模块是独立metrics-core模块的,使用时则导入metrics-healthchecks包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class HeathChecksTest extends HealthCheck {
@Override
protected Result check() throws Exception {
Random random = new Random();
if(random.nextInt(10)!=9){
return Result.healthy();
}else{
return Result.unhealthy("oh,unhealthy");
}
}
@Test
public void test() throws InterruptedException {
HealthCheckRegistry registry = new HealthCheckRegistry();
registry.register("check1",new HeathChecksTest());
registry.register("check2", new HeathChecksTest());
while (true) {
for (Map.Entry<String, Result> entry : registry.runHealthChecks().entrySet()) {
if (entry.getValue().isHealthy()) {
System.out.println(entry.getKey() + ": OK, message:"+entry.getValue());
} else {
System.err.println(entry.getKey() + ": FAIL, error message: " + entry.getValue());
}
}
Thread.sleep(1000);
}
}
}

注册两个HeathChecks,重写其check()方法为取随机数,只要不是9就为healthy,输出结果如下:

1
2
3
4
5
6
7
8
9
check1: OK, message:Result{isHealthy=true}
check2: FAIL, error message: Result{isHealthy=false, message=oh,unhealthy}
check1: OK, message:Result{isHealthy=true}
check2: OK, message:Result{isHealthy=true}
check1: OK, message:Result{isHealthy=true}
check2: OK, message:Result{isHealthy=true}
check1: OK, message:Result{isHealthy=true}
check2: OK, message:Result{isHealthy=true}
check1: OK, message:Result{isHealthy=true}

maven依赖

  • metrics-core:必须添加
  • metrics-healthchecks:用到healthchecks时添加
  • metrics-graphite:用到graphite时添加
  • org.slf4j:不添加看不到metrics-graphite包出错的log
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <properties>
    <metrics.version>3.1.0</metrics.version>
    <sl4j.version>1.7.22</sl4j.version>
    </properties>
    <dependency>
    <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-core</artifactId>
    <version>${metrics.version}</version>
    </dependency>
    <dependency>
    <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-healthchecks</artifactId>
    <version>${metrics.version}</version>
    </dependency>
    <dependency>
    <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-graphite</artifactId>
    <version>${metrics.version}</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${sl4j.version}</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>${sl4j.version}</version>
    </dependency>

参考

http://metrics.dropwizard.io/3.1.0/getting-started/
http://www.cnblogs.com/nexiyi/p/metrics_sample_1.html
http://wuchong.me/blog/2015/08/01/getting-started-with-metrics/

Java程序监控---Metrics的更多相关文章

  1. 利用jdk中工具完成Java程序监控方法记录

    转载加自己整理的部分内容,转载自:http://jiajun.iteye.com/blog/810150 记录下JConsole使用方法 一.JConsole是什么    从Java 5开始 引入了 ...

  2. JAVA 程序监控基础简述

    最近在项目中自感程序木有问题,也没有什么错误日志出来.但就是有人反映服务慢,有时连不上的情况.为了解决这么妖的问题只能去详细的看看运行中的程序到底出了什么情况,这时如果有个比较好的监控工具可以监控运行 ...

  3. Java程序监控指标

    监控指标: 1.CPU平均使用率 2.内存平均使用率 3.应用程序错误数 4.应用程序请求量 5.应用平均响应时间 6.硬件I/O指标 7.JMX 7.1.Full gc count 7.2.Full ...

  4. java程序监控tomcat中部署的项目的状态以及控制某些项目的启动停止

    原文:http://blog.csdn.net/liuyuqin1991/article/details/49280777 步骤如下: ①:首先授权用户使获得这些权限 You can find the ...

  5. zabbix 监控java程序

    http://www.tuicool.com/articles/IRnM7f http://transcoder.baidu.com/from=1012852q/bd_page_type=1/ssid ...

  6. visualvm 监控 远程 机器上的 Java 程序

    JDK里面本身就带了很多的监控工具,如JConsole等. 我们今天要讲的这款工具visualvm,就是其中的一款.但是这款工具是在JDK1.6.07及以上才有的.它能够对JAVA程序的JVM堆.线程 ...

  7. 利用JConsole工具监控java程序内存和JVM

    一.找到java应用程序对应的进程PI 性能测试应用程序访问地址:http://192.168.29.218:7070/training/ 部署的应用服务器为tomcat6.028 启动tomcat服 ...

  8. 利用btrace工具监控在线运行java程序

     一.作用 可以用于对运行中java程序进行诊断监控分析,也可以用于开发阶段查看一些异常信息或者调用过程(如有些第三方代码没有源代码,不便于debug调试). 注:如果用于对在线运行系统的诊断,需 ...

  9. (转)利用JConsole工具监控java程序内存和JVM

    转自:http://www.cnblogs.com/luihengk/p/5446279.html 一.找到java应用程序对应的进程PI 性能测试应用程序访问地址:http://192.168.29 ...

随机推荐

  1. 5)PHP,可变变量

    所谓可变变量,就是一个变量的名,又是一个变量. 可变变量的语法是php的很特殊的语法——其他语言中少见. $v1 = “abc”; //这是一个字符串变量,其内容是字符串“abc” $abc = ; ...

  2. flask学习笔记1.21

    先新建一个文件夹  templates from flask import Flask #创建Flask应用程序实例 #需要传入__name__,作用是为了确定资源所在的路径 app = Flask( ...

  3. Java BigInteger详解

    BigInteger概述 可用于无限大的整数计算 所在的包 java.math.BigInteger; 构造函数 public BigInteger(String val) 成员函数 比较大小函数 p ...

  4. Office 365 共享邮箱/日历

    一.共享邮箱 Office 365共享邮箱对于客户通过电子邮箱提出的问题,共享邮箱是一个很好的处理方式,组织中的多人可以分担监控邮箱和回复的责任,使得客户的问题更快地得到答复,而相关电子邮件都存储在一 ...

  5. [LC] 373. Find K Pairs with Smallest Sums

    You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define ...

  6. RHEL安装神器EPEL

    什么是EPEL? EPEL的全称叫 Extra Packages for Enterprise Linux .EPEL是由 Fedora 社区打造,为 RHEL 及衍生发行版如 CentOS.Scie ...

  7. AJAX数据传输

    AJAX = Asynchronous JavaScript and XML(异步的Javascript和XML) AJAX最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页 ...

  8. HTTP Status 400,400 (Bad Request)

    400 (Bad Request) 点击添加按钮转跳没反应,控制台没报错,然后在Chrome上检查发现报错了 百度了一下,发现http Status 400这个错误大多是因为,jsp的form表单提交 ...

  9. asp.net---jquery--ajax 实现滚动条滚动到底部分页显示

    前台:aspx页面 var bgtime = $(" #date1 ").val(); var overtime = $(" #date2 ").val(); ...

  10. django操作非ORM创建的表

    问题:django的ORM怎么连接已存在的表,然后进行增删查改操作?     工作中会遇见很多二次开发的时候,表都是已经创建好的,用django的ORM进行二次开发,怎么操作数据库中的表呢? 下面介绍 ...