关于Metrics更多的内容可以查看 metrics-getting-started
代码地址
因为每个例子涉及代码较多,且包含测试用例,如果都贴到文章中内容过多,所以只贴出了部分代码。全部的代码在这里: https://gitee.com/daifyutils/springboot-samples。
此篇文章所属模块为:base-metrics
Metrics提供了一个强大的工具包,用于衡量生产环境中关键组件的行为。其提供了一个工具包,让我们可以对服务中的一些行为进行监控和统计。按照官方的说法Metrics为Jetty、Logback、Log4j、Apache HttpClient、Ehcache、JDBI、Jersey等多个开源库提供支持。
简单的说,我们将各个业务模块的指标或者数据收集起来,设置到Metrics中,然后通过Metrics获取相关的统计结果。其实不用Metrics我们也可以实现相关的数据统计,但是Metrics提供了比较完善的分析方法,我们可以主要关注收集指标的过程。
Metrics主要的统计内容
类型 | 统计内容 |
---|---|
Gauges | 度量指标,简单的值返回 |
Counter | 简单计数器 |
Histograms | 直方图格式数据,用来统计输入指标中的值分布 |
Timers | 计时器,以直方图格式数据显示指标的时间数据 |
Meters | 速率统计,比如TPS |
Metrics主要的报告格式
<dependencies>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>${metrics.version}</version>
</dependency>
</dependencies>
新增MetricRegistry
MetricRegistry是Metric用来存放metrics数据的容器
MetricRegistry registry = new MetricRegistry();
添加度量信息
使用对应分析度量的同名方法,将需要添加的度量设置到MetricRegistry中。在设置分析度量的时候需要提供一个名称作为分析度量唯一标识。下面是一个设置counter
分析度量的代码
// 创建一个度量
registry = new MetricRegistry();
// 注册一个counter
pendingJobs = registry.counter(MetricRegistry.name(Queue.class,"jobs","size"));
Metric提供了ConsoleReporter
工具方便我们可以快速的获取分析度量中的内容,这个类MetricBase
在下面其他度量的使用示例中也会作为他们基类使用
public class MetricBase {
public static MetricRegistry registry;
public static void initMetric(){
// 创建一个度量
registry = new MetricRegistry();
// 注册到控制台中
ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
// 每5秒输出一次结果
reporter.start(5, TimeUnit.SECONDS);
}
}
计数器,内部使用AtomicLong提供了增加和减少值的方法,用来记录一个值的变化
public class CounterTest extends MetricBase{
private static Queue<String> queue = new LinkedBlockingQueue<String>();
private static Counter counter;
private static Random random = new Random();
public static void main(String[] args) throws InterruptedException {
initMetric();
// 注册一个counter
counter = registry.counter(MetricRegistry.name(Queue.class,"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++;
}
}
private static void addJob(String job) {
counter.inc();
queue.offer(job);
}
private static String takeJob() {
counter.dec();
return queue.poll();
}
}
输出内容
22-5-8 10:37:05 ================================================================
-- Counters --------------------------------------------------------------------
java.util.Queue.jobs.size
count = 33
返回参数
参数 | 获取方法 | 意义 |
---|---|---|
count | counter.getCount() | 计数器当前值 |
返回单一值,用来获取某些参数中当前的值,创建时需要实现获取值的方法,对外只提供获取值方法
public class GaugesTest extends MetricBase{
private static Queue<String> taskList = new LinkedList<String>();
public static void main(String[] args) throws InterruptedException {
initMetric();
Gauge gauge = () -> taskList.size();
// 注册统计内容
registry.register(MetricRegistry.name(GaugesTest.class, "queue", "size"),gauge);
while(true){
Thread.sleep(1000);
taskList.add("Job-xxx");
}
}
}
输出内容
22-5-8 10:40:02 ================================================================
-- Gauges ----------------------------------------------------------------------
dai.samples.metrics.GaugesTest.queue.size
value = 9
返回参数
参数 | 获取方法 | 意义 |
---|---|---|
count | gauge.getValue() | 计数器当前值 |
直方图格式数据,用来统计输入指标中的值分布,提供最小、最大等参数外还提供了75百分位到99百分位、99.9百分位的值
public class HistogramsTest extends MetricBase{
public static Random random = new Random();
public static void main(String[] args) throws InterruptedException {
initMetric();
// 注册一个Histogram,ExponentiallyDecayingReservoir 直方图的统计方式
Histogram histogram = new Histogram(new ExponentiallyDecayingReservoir());
registry.register(MetricRegistry.name(HistogramsTest.class, "request", "histogram"), histogram);
while(true){
Thread.sleep(1000);
histogram.update(random.nextInt(100000));
}
}
}
输出内容
-- Histograms ------------------------------------------------------------------
dai.samples.metrics.HistogramsTest.request.histogram
count = 49
min = 4177
max = 99701
mean = 50089.35
stddev = 31115.63
median = 39422.00
75% <= 83459.00
95% <= 97734.00
98% <= 97825.00
99% <= 99701.00
99.9% <= 99701.00
返回参数
参数 | 获取方法 | 意义 |
---|---|---|
count | histogram.getCount(); | 输入指标总数 |
min | histogram.getSnapshot().getMin(); | 输入指标最小值 |
max | histogram.getSnapshot().getMax(); | 输入指标最大值 |
mean | histogram.getSnapshot().getMean(); | 输入指标平均值 |
stddev | histogram.getSnapshot().getStdDev(); | 输入指标中值的标准偏差 |
median | histogram.getSnapshot().getMedian(); | 输入指标中位数 |
75% | histogram.getSnapshot().get75thPercentile(); | 输入指标75百分位 |
95% | histogram.getSnapshot().get95thPercentile(); | 输入指标95百分位 |
98% | histogram.getSnapshot().get98thPercentile(); | 输入指标98百分位 |
99% | histogram.getSnapshot().get99thPercentile(); | 输入指标99百分位 |
99.9% | histogram.getSnapshot().get999thPercentile(); | 输入指标99.9百分位 |
速率统计,比如TPS。Meters会统计最近1分钟,5分钟,15分钟,还有全部时间的速率。
public class MetersTest extends MetricBase{
private static Random random = new Random();
public static void main(String[] args) throws InterruptedException {
initMetric();
Meter meterTps = registry.meter(MetricRegistry.name(MetersTest.class,"request","tps"));
while(true){
request(meterTps,random.nextInt(5));
Thread.sleep(1000);
}
}
private static void request(Meter meter, int n){
while(n > 0){
System.out.println("request");
meter.mark();
n--;
}
}
}
输出内容
-- Meters ----------------------------------------------------------------------
dai.samples.metrics.MetersTest.request.tps
count = 17
mean rate = 1.70 events/second
1-minute rate = 1.62 events/second
5-minute rate = 1.60 events/second
15-minute rate = 1.60 events/second
返回参数
参数 | 获取方法 | 意义 |
---|---|---|
count | gauge.getValue() | 计数器当前值 |
mean rate | meterTps.getMeanRate() | 平均TPS |
1-minute rate | meterTps.getOneMinuteRate() | 最近1分钟TPS |
5-minute rate | meterTps.getFiveMinuteRate() | 最近5分钟TPS |
15-minute rate | meterTps.getFifteenMinuteRate() | 最近15分钟TPS |
计时器,以直方图格式数据显示指标的时间数据,比如某些操作的耗时等,同时也支持显示Meters的数据分析
public class TimersTest extends MetricBase{
public static Random random = new Random();
public static void main(String[] args) throws InterruptedException {
initMetric();
Timer timer = registry.timer(MetricRegistry.name(TimersTest.class,"get-latency"));
Timer.Context ctx;
while(true){
ctx = timer.time();
Thread.sleep(random.nextInt(1000));
ctx.stop();
}
}
}
输出内容
22-5-8 11:42:09 ================================================================
-- Timers ----------------------------------------------------------------------
dai.samples.metrics.TimersTest.get-latency
count = 41
mean rate = 2.05 calls/second
1-minute rate = 2.82 calls/second
5-minute rate = 2.96 calls/second
15-minute rate = 2.99 calls/second
min = 4.47 milliseconds
max = 905.26 milliseconds
mean = 480.30 milliseconds
stddev = 270.88 milliseconds
median = 536.26 milliseconds
75% <= 676.99 milliseconds
95% <= 871.15 milliseconds
98% <= 905.26 milliseconds
99% <= 905.26 milliseconds
99.9% <= 905.26 milliseconds
返回参数
参数 | 获取方法 | 意义 |
---|---|---|
count | gauge.getValue() | 计数器当前值 |
mean rate | timer.getMeanRate() | 平均TPS |
1-minute rate | timer.getOneMinuteRate() | 最近1分钟TPS |
5-minute rate | timer.getFiveMinuteRate() | 最近5分钟TPS |
15-minute rate | timer.getFifteenMinuteRate() | 最近15分钟TPS |
min | timer.getSnapshot().getMin(); | 输入时间指标最小值 |
max | timer.getSnapshot().getMax(); | 输入时间指标最大值 |
mean | timer.getSnapshot().getMean(); | 输入时间指标平均值 |
stddev | timer.getSnapshot().getStdDev(); | 输入时间指标中值的标准偏差 |
median | timer.getSnapshot().getMedian(); | 输入时间指标中位数 |
75% | timer.getSnapshot().get75thPercentile(); | 输入时间指标75百分位 |
95% | timer.getSnapshot().get95thPercentile(); | 输入时间指标95百分位 |
98% | timer.getSnapshot().get98thPercentile(); | 输入时间指标98百分位 |
99% | timer.getSnapshot().get99thPercentile(); | 输入时间指标99百分位 |
99.9% | timer.getSnapshot().get999thPercentile(); | 输入时间指标99.9百分位 |