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


    1         <!-- metrics -->
    2         <dependency>
    3             <groupId>io.dropwizard.metrics</groupId>
    4             <artifactId>metrics-core</artifactId>
    5         </dependency>
    View Code

    这里,依托于springboot,版本号是3.1.2

    一、meter类metrics

    作用:统计最近1分钟(m1),5分钟(m5),15分钟(m15),还有全部时间的速率(速率就是平均值)

    例如:qps

    线程安全:mark()方法中的四个操作都是基于CAS实现,统计线程安全。

     1 package com.xxx.secondboot.metrics;
     2 
     3 import java.util.concurrent.TimeUnit;
     4 
     5 import com.codahale.metrics.ConsoleReporter;
     6 import com.codahale.metrics.Meter;
     7 import com.codahale.metrics.MetricRegistry;
     8 
     9 /**
    10  * Meter
    11  * 作用:度量速率(例如,tps)
    12  * Meters会统计最近1分钟(m1),5分钟(m5),15分钟(m15),还有全部时间的速率(速率就是平均值)。
    13  */
    14 public class TestMeter {
    15     public static void main(String[] args) throws InterruptedException {
    16         final MetricRegistry registry = new MetricRegistry();//其实就是一个metrics容器,因为该类的一个属性final ConcurrentMap<String, Metric> metrics,在实际使用中做成单例就好
    17         ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
    18                                   .convertRatesTo(TimeUnit.SECONDS)
    19                                   .convertDurationsTo(TimeUnit.MILLISECONDS)
    20                                   .build();
    21         reporter.start(1, TimeUnit.SECONDS);//从启动后的1s后开始(所以通常第一个计数都是不准的,从第二个开始会越来越准),每隔一秒从MetricRegistry钟poll一次数据
    22         Meter meterTps = registry.meter(MetricRegistry.name(TestMeter.class, "request", "tps"));//将该Meter类型的指定name的metric加入到MetricsRegistry中去
    23 
    24         System.out.println("执行与业务逻辑");
    25         
    26         while(true){
    27             meterTps.mark();//总数以及m1,m5,m15的数据都+1
    28             Thread.sleep(500);
    29         }
    30     }
    31 }

    注意:

    • MetricRegistry是一个所有metrics的容器(通常设为单例)
    • ConsoleReporter根据指定的打印速率(在start方法中指定)将metrics打印到console
    • metrics name需要指定,这对于在statsd的统计部分以及聚合函数的选择都有用,上边的name()方法实际上是将类的全类名与后续的不定参数以"."拼接而成,这里metric name就是"com.xxx.secondboot.metrics.TestMeter.request.tps"
    • mark方法:总数count和m1,m5,m15的数据都+1

    report.start()方法源码:

     1     public void start(long period, TimeUnit unit) {
     2         executor.scheduleAtFixedRate(new Runnable() {
     3             @Override
     4             public void run() {
     5                 try {
     6                     report();
     7                 } catch (RuntimeException ex) {
     8                     LOG.error("RuntimeException thrown from {}#report. Exception was suppressed.", ScheduledReporter.this.getClass().getSimpleName(), ex);
     9                 }
    10             }
    11         }, period, period, unit);
    12     }
    scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

    方法含义:

    • 在服务启动的initialDelay unit(这里就是1s)后开始每隔period unit执行一次command(所以,通常第一次统计都不准确,从第二次开始变得准确)
    • reporter值主动从MetricRegistry中poll数据的
    • 真正的report是被synchronized块包起来的(也就是线程安全的),而report的内部逻辑随着report的类型不同而不同(例如,ConsoleReporter就是将四种数据打印到console)

    启动服务,输出:(从系统时间开始输出,该例子正好是在01s开始输出的)

    16-10-3 20:23:07 ===============================================================
    
    -- Meters ----------------------------------------------------------------------
    com.xxx.secondboot.metrics.TestMeter.request.tps
                 count = 14
             mean rate = 2.00 events/second
         1-minute rate = 2.00 events/second
         5-minute rate = 2.00 events/second
        15-minute rate = 2.00 events/second

    7s内输出14,每秒count+2,符合程序!!!

    二、gauge类metrics

    作用:返回一个瞬时值(就是一个具体值)

    例如:某一时刻的队列size

    线程安全:只是做读操作,线程安全

     1 package com.xxx.secondboot.metrics;
     2 
     3 import java.time.LocalDateTime;
     4 import java.util.LinkedList;
     5 import java.util.Queue;
     6 import java.util.concurrent.TimeUnit;
     7 
     8 import com.codahale.metrics.ConsoleReporter;
     9 import com.codahale.metrics.Gauge;
    10 import com.codahale.metrics.MetricRegistry;
    11 
    12 /**
    13  * Gauge
    14  * 作用:只返回一个简单值(一个瞬时值)
    15  * eg:返回队列的size
    16  */
    17 public class TestGauge {
    18 
    19     public static Queue<String> queue = new LinkedList<>();//队列
    20 
    21     public static void main(String[] args) {
    22         MetricRegistry registry = new MetricRegistry();
    23         ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
    24                 .convertRatesTo(TimeUnit.SECONDS)
    25                 .convertDurationsTo(TimeUnit.MILLISECONDS)
    26                 .build();
    27         reporter.start(1, TimeUnit.SECONDS);
    28 
    29         registry.register(MetricRegistry.name(TestGauge.class, "queue", "size"), new Gauge<Integer>() {
    30             public Integer getValue() {
    31                 return queue.size();
    32             }
    33         });
    34 
    35         while (true) {
    36             try {
    37                 Thread.sleep(1000);
    38                 queue.add("job - " + LocalDateTime.now());
    39             } catch (InterruptedException e) {
    40                 e.printStackTrace();
    41             }
    42         }
    43     }
    44 }

    注意:

    • 在registry()的时候,可以直接将一个类型的Metric直接注入到容器中,其name就是registry()的第一个参数

    输出:

    16-10-3 20:57:27 ===============================================================
    
    -- Gauges ----------------------------------------------------------------------
    com.xxx.secondboot.metrics.TestGauge.queue.size
                 value = 1

    三、counter类metrics

    作用:gauge的AtomicLong实例(Counter 只是用 Gauge 封装了 AtomicLong),可用于加(inc())减(dec())

    例如:获得队列长度(此处的获取要比使用gauge通过size()方法获取高效很多,后者size()方法的获取大多数是O(n)),方法执行成功失败次数(这个就是gauge无法做的)

    作用:AtomicLong基于CAS,线程安全

     1 package com.xxx.secondboot.metrics;
     2 
     3 import java.util.Queue;
     4 import java.util.concurrent.LinkedBlockingQueue;
     5 import java.util.concurrent.TimeUnit;
     6 
     7 import com.codahale.metrics.ConsoleReporter;
     8 import com.codahale.metrics.Counter;
     9 import com.codahale.metrics.MetricRegistry;
    10 
    11 /**
    12  * counter:
    13  * 作用:计数器(用gauge封装了AtomicLong)
    14  */
    15 public class TestCounters {
    16     public static Queue<String> queue = new LinkedBlockingQueue<>();
    17     public static Counter       counter;//计算queue的大小
    18 
    19     public static void main(String[] args) throws InterruptedException {
    20         MetricRegistry registry = new MetricRegistry();
    21         ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
    22                 .convertRatesTo(TimeUnit.SECONDS)
    23                 .convertDurationsTo(TimeUnit.MILLISECONDS)
    24                 .build();
    25         reporter.start(1, TimeUnit.SECONDS);
    26         counter = registry.counter(MetricRegistry.name(TestCounters.class, "queue", "size"));
    27 
    28         int num = 0;
    29         while (true) {
    30             if (num < 10) {
    31                 queue.add("job - " + num);
    32                 counter.inc();
    33             } else if (num > 10 && num < 16) {
    34                 queue.poll();
    35                 counter.dec();
    36             } else {
    37                 queue.add("job - " + num);
    38                 counter.inc();
    39             }
    40             num++;
    41             Thread.sleep(500);
    42         }
    43     }
    44 }

    输出:

    16-10-3 21:15:17 ===============================================================
    
    -- Counters --------------------------------------------------------------------
    com.xxx.secondboot.metrics.TestCounters.queue.size
                 count = 4

    四、histogram类metrics(使用较少)

    作用:计算执行次数count、最小值min,最大值max,平均值mean,方差stddev,中位数median,75百分位, 90百分位, 95百分位, 98百分位, 99百分位, 和 99.9百分位的值 

    例如:统计某个函数的执行耗时,以上这些值通常会是执行时间,如min是最短执行时间等

    线程:update的操作需要获取锁,操作之后释放锁。线程安全。

     1 package com.xxx.secondboot.metrics;
     2 
     3 import java.util.Random;
     4 import java.util.concurrent.TimeUnit;
     5 
     6 import com.codahale.metrics.ConsoleReporter;
     7 import com.codahale.metrics.ExponentiallyDecayingReservoir;
     8 import com.codahale.metrics.Histogram;
     9 import com.codahale.metrics.MetricRegistry;
    10 
    11 /**
    12  * Histogram
    13  * 作用:计算执行次数count、最小值min,最大值max,平均值mean,方差stddev,中位数median,75百分位, 90百分位, 95百分位, 98百分位, 99百分位, 和 99.9百分位的值
    14  */
    15 public class TestHistogram {
    16     public static void main(String[] args) throws InterruptedException {
    17         MetricRegistry registry = new MetricRegistry();
    18         ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
    19                 .convertRatesTo(TimeUnit.SECONDS)
    20                 .convertDurationsTo(TimeUnit.MILLISECONDS)
    21                 .build();
    22         reporter.start(1, TimeUnit.SECONDS);
    23         
    24         Histogram histogram = new Histogram(new ExponentiallyDecayingReservoir());//95%
    25         registry.register(MetricRegistry.name(TestHistogram.class, "request","histogram"), histogram);
    26         
    27         Random random = new Random();
    28         while(true){
    29             Thread.sleep(1000);
    30             histogram.update(random.nextInt(10000));
    31         }
    32     }
    33 }

    输出:

     1 16-10-3 21:26:05 ===============================================================
     2 
     3 -- Histograms ------------------------------------------------------------------
     4 com.xxx.secondboot.metrics.TestHistogram.request.histogram
     5              count = 3
     6                min = 685
     7                max = 6754
     8               mean = 3149.05
     9             stddev = 2584.36
    10             median = 2078.00
    11               75% <= 6754.00
    12               95% <= 6754.00
    13               98% <= 6754.00
    14               99% <= 6754.00
    15             99.9% <= 6754.00

    五、timer类metrics

    作用:meter和histogram的组合体

    例如:统计某个函数的qps和执行耗时。

    线程安全:meter和histogram都安全,所以也线程安全

     1 package com.xxx.secondboot.metrics;
     2 
     3 import java.util.concurrent.TimeUnit;
     4 
     5 import com.codahale.metrics.ConsoleReporter;
     6 import com.codahale.metrics.MetricRegistry;
     7 import com.codahale.metrics.Timer;
     8 
     9 /**
    10  * Timers
    11  * 作用:histogram和meter的组合体
    12  */
    13 public class TestTimer {
    14     public static void main(String[] args) throws InterruptedException {
    15         MetricRegistry registry = new MetricRegistry();
    16         ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
    17         reporter.start(1, TimeUnit.SECONDS);
    18         
    19         Timer timer = registry.timer(MetricRegistry.name(TestTimer.class, "get-latency"));
    20         Timer.Context ctx = timer.time();
    21         
    22         try{
    23             Thread.sleep(2000);
    24         }finally{
    25             ctx.stop();
    26         }
    27     }
    28 }

    输出:

     1 -- Timers ----------------------------------------------------------------------
     2 com.xxx.secondboot.metrics.TestTimer.get-latency
     3              count = 0
     4          mean rate = 0.00 calls/second
     5      1-minute rate = 0.00 calls/second
     6      5-minute rate = 0.00 calls/second
     7     15-minute rate = 0.00 calls/second
     8                min = 0.00 milliseconds
     9                max = 0.00 milliseconds
    10               mean = 0.00 milliseconds
    11             stddev = 0.00 milliseconds
    12             median = 0.00 milliseconds
    13               75% <= 0.00 milliseconds
    14               95% <= 0.00 milliseconds
    15               98% <= 0.00 milliseconds
    16               99% <= 0.00 milliseconds
    17             99.9% <= 0.00 milliseconds

    总结:

    •  统计某个函数被调用的频率(TPS),使用Meters。
    • 统计某个方法的耗时,使用Histograms。--注意时间是以纳秒为单位的
    • 既要统计某个方法的TPS又要统计其耗时时,使用Timers。--注意时间是以纳秒为单位的
    • counter用于计数
    • gauge只用于记录瞬时值

     counter与gauge:

    • 在某些时候,只能用gauge,比如说这个值是在第三方包提供的,例如guava cache的cache size(而恰好我们将该cache集成在spring cache中,通过注解来使用了),无法用哪个counter来测量
    • 在某些时候,只能用counter,比如说一个方法的执行成功与失败次数

    histogram:

    在统计中位数以及95%这样的数据的时候,通常需要把所有的数据拿出来,然后进行运算(在大量的数据下该方法失效,所以采用了水库采集法--reservoir sampling,通过维护一个小的、可管理的水库来代表全部统计数据),具体采集法有以下几种:

    • Uniform Reservoirs:随机选择具有线性递减概率的储层的值,仅用于长时间的测量。测量统计数据最近是不是发生了变化,不要使用这个(使用下边的指数衰减水库)。
    • Exponentially Decaying Reservoirs(指数衰减水库):该水库采集的数据可以代表大约最后5分钟的全部数据。该水库也是Times 类metrics使用histogram的默认选择水库。
    • Sliding Window Reservoirs:代表过去n次测量的数据
    • Sliding Time Window Reservoirs:严格的代表过去n秒内的数据(注意与指数衰减库的区别,该方法严格的记录过去的每一秒的数据(而指数衰减其实还是在最后5min进行抽样),所以在高频下可能需要更多内存,而且也是最慢的水库类型)

    参考:

    http://metrics.dropwizard.io/3.1.0/getting-started/

    http://wuchong.me/blog/2015/08/01/getting-started-with-metrics/

  • 相关阅读:
    springBoot 2.1.5 pom 文件 unknown 错误
    @HystrixCommand 不能被导包
    SQL数据库连接语句
    ADO.NET中COMMAND对象的ExecuteNonQuery、ExcuteReader和ExecuteScalar方法
    重载和重写的区别
    抽象类和接口的相同点和不同点
    结构详解
    简单工厂和抽象工厂的区别
    DataRead和DataSet的异同
    什么是Web Server
  • 原文地址:https://www.cnblogs.com/java-zhao/p/5929723.html
Copyright © 2020-2023  润新知