第三十五章 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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM