微服务架构时代,服务运行状况通过服务埋点设计越来越多。熔断器,服务网格,API网关 怎么洞悉他们运行状态,仅通过传统外部Agent采集,实现复杂,双方对接开发成本高,可能远远满足不了要求,java-metrics可以有效满足这些场景.
介绍
Metrics是一个给JAVA服务的各项指标提供度量工具的包,在JAVA代码中嵌入Metrics代码,可以方便的对业务代码的各个指标进行监控,同时,Metrics能够很好的跟Ganglia、Graphite,influxdb 优秀监控工具整合使用,方便的提供图形化接口。基本使用方式直接将core包(目前稳定版本3.0.1)导入pom文件即可,配置如下:
<dependency>
<groupId>com.codahale.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.0.1</version>
</dependency>
基本功能
core包主要提供如下核心功能:
- Metrics Registries类似一个metrics容器,维护一个Map,可以是一个服务 或者一个静态实例。
- 支持五种metric类型:Gauges、Counters、Meters、Histograms和Timers。
- 支持多种指标数据输出:JMX、Console,CSV文件和SLF4J loggers 等输出方式;
- 支持自定义扩展输出,同时也可以和Ganglia,Graphite 结合使用
Metrics类型(5种):
1. Gauges
Gauges是一个最简单的计量,一般用来统计瞬时状态的数据信息
GaugesExample.java
package example;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricRegistry;
/**
* User: guoenzhou
* 测试Gauges,实时统计pending状态的job个数
*/
public class GaugesExample {
/**
* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map
*/
private static final MetricRegistry metrics = new MetricRegistry();
private static Queue<String> queue = new LinkedBlockingDeque<String>();
/**
* ConsoleReporter 在控制台上打印输出
*/
private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();
public static void main(String[] args) throws InterruptedException {
reporter.start(10, TimeUnit.SECONDS);
//实例化一个Gauge
Gauge<Integer> gauge = new Gauge<Integer>() {
public Integer getValue() {
return queue.size();
}
};
//注册到容器中
metrics.register(MetricRegistry.name(GaugesExample.class, "pending.job", "size"), gauge);
//模拟数据
for (int i=0; i<1000; i++){
queue.add("a");
Thread.sleep(1000);
}
}
}
通过以上步骤将会向MetricsRegistry容器中注册一个指标名字为 example.GaugesExample.pending.job.size 的metrics,实时获取队列长度的指标。
输出:
-- Gauges ----------------------------------------------------------------------
example.TestGauges.pending-job.size
value = 340
18-1-2 15:03:17 ================================================================
-- Gauges ----------------------------------------------------------------------
example.TestGauges.pending-job.size
value = 350
18-1-2 15:03:27 ================================================================
-- Gauges ----------------------------------------------------------------------
example.TestGauges.pending-job.size
value = 360
2. Counter
Counter是Gauge的一个特例,维护一个计数器,可以通过inc()和dec()方法对计数器做修改。
package example;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;
public class TestCounter {
/**
* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map
*/
private static final MetricRegistry metrics = new MetricRegistry();
/**
* 在控制台上打印输出
*/
private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();
/**
* 实例化一个counter,同样可以通过如下方式进行实例化再注册进去
* pendingJobs = new Counter();
* ;
*/
private static Counter pendingJobs = metrics.counter(MetricRegistry.name(TestCounter.class, "pedding。jobs"));
private static Queue<String> queue = new LinkedList<String>();
public static void add(String str) {
pendingJobs.inc();
queue.offer(str);
}
public static String take() {
pendingJobs.dec();
return queue.poll();
}
public static void main(String[]args) throws InterruptedException {
reporter.start(3, TimeUnit.SECONDS);
while(true){
add("1");
Thread.sleep(1000);
}
}
}
ConsoleReporter打印输出
18-1-2 14:11:02 ================================================================
-- Counters --------------------------------------------------------------------
example.TestCounter.pedding。jobs
count = 4
18-1-2 14:11:05 ================================================================
-- Counters --------------------------------------------------------------------
example.TestCounter.pedding。jobs
count = 7
3. Meters
Meters用来度量某个时间段的平均处理次数(request per second),每1、5、15分钟的TPS。比如一个service的请求数,通过metrics.meter()实例化一个Meter之后,然后通过meter.mark()方法就能将本次请求记录下来
package example;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
public class TestMeters {
/**
* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map
*/
private static final MetricRegistry metrics = new MetricRegistry();
/**
* 在控制台上打印输出
*/
private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();
/**
* 实例化一个Meter
*/
private static final Meter requests = metrics.meter(MetricRegistry.name(TestMeters.class, "request"));
public static void handleRequest() {
requests.mark();
}
public static void main(String[] args) throws InterruptedException {
reporter.start(5, TimeUnit.SECONDS);
while(true){
handleRequest();
Thread.sleep(100);
}
}
}
输出
18-1-2 14:51:25 ================================================================
-- Meters ----------------------------------------------------------------------
example.TestMeters.request
count = 51
mean rate = 10.15 events/second
1-minute rate = 10.00 events/second
5-minute rate = 10.00 events/second
15-minute rate = 10.00 events/second
18-1-2 14:51:30 ================================================================
-- Meters ----------------------------------------------------------------------
example.TestMeters.request
count = 100
mean rate = 9.99 events/second
1-minute rate = 10.00 events/second
5-minute rate = 10.00 events/second
15-minute rate = 10.00 events/second
4. Histograms
Histograms主要使用来统计数据的分布情况,最大值、最小值、平均值、中位数,百分比(75%、90%、95%、98%、99%和99.9%)。 场景:统计API请求响应时间
package example;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.MetricRegistry;
/**
* 测试Histograms
*/
public class TestHistograms {
/**
* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map
*/
private static final MetricRegistry metrics = new MetricRegistry();
/**
* 在控制台上打印输出
*/
private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();
/**
* 实例化一个Histograms
*/
private static final Histogram responseTimeMetric= metrics.histogram(MetricRegistry.name(TestHistograms.class, "responsetime"));
public static void handleRequest(double random) {
responseTimeMetric.update((int) (random*100));
}
public static void main(String[] args) throws InterruptedException {
//ConsoleReporter启动 每5秒report输出一次
reporter.start(5, TimeUnit.SECONDS);
Random rand = new Random();
do {
handleRequest(rand.nextDouble());
Thread.sleep(100);
} while(true);
}
}
输出
18-1-2 14:36:35 ================================================================
-- Histograms ------------------------------------------------------------------
example.TestHistograms.random
count = 51
min = 0
max = 99
mean = 52.34
stddev = 31.46
median = 49.00
75% <= 83.00
95% <= 98.00
98% <= 99.00
99% <= 99.00
99.9% <= 99.00
5. Timers
Timers主要是用来统计某一块代码段的执行时间以及其分布情况,具体是基于Histograms和Meters来实现的。
Report类型
ConsoleReporter
前面例子中已经涉及
JmxReporter
JmxReporter 是将指标值输出到JDK Mbeans 中,可以通过JDK自带的jvisualvm.exe 查看.
package example.reporter;
import java.util.LinkedList;
import java.util.Queue;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.JmxReporter;
import com.codahale.metrics.MetricRegistry;
import example.TestGauges;
public class TestJmxReporter {
/**
* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map
*/
private static final MetricRegistry metrics = new MetricRegistry();
private static Queue<String> queue = new LinkedList<String>();
/**
* 在控制台上打印输出
*/
private static JmxReporter reporter = JmxReporter.forRegistry(metrics).build();
public static void main(String[] args) throws InterruptedException {
reporter.start();
//实例化一个Gauge
Gauge<Integer> gauge = new Gauge<Integer>() {
public Integer getValue() {
return queue.size();
}
};
//注册到容器中
metrics.register(MetricRegistry.name(TestGauges.class, "pending.job", "size"), gauge);
//模拟数据
for (int i=0; i<1000; i++){
queue.add("a");
Thread.sleep(1000);
}
}
}
输出查看
SLF4JReporter
final Slf4jReporter reporter = Slf4jReporter.forRegistry(registry)
.outputTo(LoggerFactory.getLogger("com.example.metrics"))
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(1, TimeUnit.MINUTES);
GraphiteReporter
该metrics-graphite模块提供GraphiteReporter,它允许您的应用程序不断将度量标准值传输到Graphite服务器
Metrics模块
- metrics-json提供了json格式的序列化。 以及为其它库提供度量的能力
- metrics-ehcache
- metrics-httpclient
- metrics-jdbi
- metrics-jersey
- metrics-jetty
- metrics-log4j
- metrics-logback
- metrics-jvm
- Metrics Servlets
第三方库
- metrics-librato 提供Librato Metrics报表
- Metrics Spring Integration 提供了Spring的集成
- sematext-metrics-reporter 提供了SPM报表.
- wicket-metrics提供Wicket应用.
- metrics-guice 提供Guice集成.
- metrics-scala 提供了为Scala优化的API.
metrics源码解析
//TODO
场景应用
Metrics与Spring集成
Metrics和graphite监控
Metrics+InfluxDB+Grafana 监控
Metrics+openFalcon 监控
//TODO
Java服务端监控平台设计
参考资料
官方文档:http://metrics.dropwizard.io/3.2.3/ 代码库:https://github.com/dropwizard/metrics
更详细参考资料:
https://www.cnblogs.com/nexiyi/p/metrics_sample_1.html
http://www.cnblogs.com/nexiyi/p/metrics_sample_2.html
http://colobu.com/2014/08/08/Metrics-and-Spring-Integration/
metrics 与spring集成 http://blog.youkuaiyun.com/zyt425916200/article/details/54392047