Metrics監控工具介紹與使用

目錄

  1. Metrics
    1. 介紹
    2. 使用
      1. Metric Registries
      2. Gauges
      3. Counter
      4. Histograms
      5. Meters
      6. Timers
      7. 監控輸出方式

Metrics

介紹

監控工具。Metrics提供了強大的工具用於監控重要組件的行爲。Metrics依賴Jetty, Logback, Log4j, Apache HttpClient, Ehcache, JDBI, Jersey和Graphite,提供一個可視化程序執行情況。

Metrics可以統計TPS,方法執行次數,隊列情況,方法執行時間等,可以支持生成儀表盤格式,柱狀圖格式的數據。並且支持將此類數據輸出到控制檯、日誌文件、CSV格式文件中。也可以通過簡單配置提供返回JMX格式的接口。

使用

Metric Registries

Metric註冊中心是Metric工具的入口,用於提供創建各種監控工具和存儲這些監控工具。通常情況下一個應用只需要一個Metrics註冊中心,並且應用初始化時初始化註冊中心。如果應用需要多個Metric註冊中心,可以使用SharedMetricRegistries來管理。SharedMetricRegistries維護一個繼承自ConcurrentMap的REGISTRIES對象,key是註冊中心的名字,value是註冊中心實例。SharedMetricRegistries提供一些基本的維護註冊中心(添加和刪除註冊中心)方法。

如下是Spring Boot利用註冊方式初始化一個Metric Registries:

    @Configuration
    public class Configuration {
    
        @Bean
        MetricRegistry metricRegistry() {
            return new MetricRegistry();
        }
    }

Metric Registries利用ConcurrentMap來存儲所有的監控,key是監控的名字,value是監控的實例。所以註冊中心要求每個監控的名字必須唯一。MetricRegistry提供了生成監控名稱的方法

   /**
     * Concatenates elements to form a dotted name, eliding any null values or empty strings.
     *
     * @param name  the first element of the name
     * @param names the remaining elements of the name
     * @return {@code name} and {@code names} concatenated by periods
     */
    public static String name(String name, String... names) {
        final StringBuilder builder = new StringBuilder();
        append(builder, name);
        if (names != null) {
            for (String s : names) {
                append(builder, s);
            }
        }
        return builder.toString();
    }

官網推薦使用 類對象+業務對象+業務對象屬性來命名監控名稱,比如:

    public static void main(String[] args) {
    
        String name = MetricRegistry.name(Queue.class, "requests", "size");
    
    }

Gauges

gauge是一種只監控統計一個唯獨的監控數據類型,比如可以統計程序啓動後接口被訪問了多少次:

    @org.springframework.context.annotation.Configuration
    public class Configuration {
    
        @Bean
        MetricRegistry metricRegistry() {
            return new MetricRegistry();
        }
    
        // 配置控制檯輸出監控指標
        @Bean
        ConsoleReporter consoleReporter() {
            ConsoleReporter reporter = ConsoleReporter.forRegistry(metricRegistry())
                    .convertRatesTo(TimeUnit.SECONDS)
                    .convertDurationsTo(TimeUnit.MILLISECONDS)
                    .build();
            reporter.start(5, TimeUnit.SECONDS);
            return reporter;
        }
    }
    
    @RestController
    public class GaugesController {
    
        // test方法被訪問的次數
        private int countTest = 0;
    
        // register在項目啓動過程中已經被Spring初始化並加入到bean容器中
        public GaugesController(MetricRegistry registry) {
            registry.register("test", (Gauge<Integer>) () -> countTest);
    
        }
    
        @RequestMapping(value = "/gauges/test")
        public String gauges() {
            countTest++;
            return String.valueOf(countTest);
        }
    }

控制檯每5秒鐘輸出結果,隨着每次點擊 test值不斷增加。本次點擊測試的輸入如下:

#第一次輸入(本人點擊了一次)
-- Gauges ----------------------------------------------------------------------
test
             value = 1

# 第二次的5秒輸出(本人累計點擊7次)     
-- Gauges ----------------------------------------------------------------------
test
             value = 7

這裏只是舉了一個使用gauge的例子,Metrics有單獨提供用於統計次數的監控數據類型Counters。

gauges還有一種可以一個監控兩組數據比率的監控數據類型RatioGauge,以下列舉兩個接口調用次數的比例來練習RatioGauge的使用。

    @RestController
    public class GaugesController {
    
        public GaugesController(MetricRegistry registry) {
            registry.register("test", (Gauge<Integer>) () -> countTest);
    
            // 註冊一個RatioGauge
            RatioGauge ratioGauge = new RatioGauge() {
                @Override
                protected Ratio getRatio() {
                    // ratio返回 接口/gauges/ratio調用次數除以
                    return Ratio.of(countRatioGauges, countTest + countRatioGauges);
                }
            };
            registry.register("test2", ratioGauge);
        }
    
        @GetMapping(value = "/gauges/test")
        public String gauges() {
            countTest++;
            return String.valueOf(countTest);
        }
    
        // 增加一個接口,並統計調用次數
        @GetMapping(value = "/gauges/ratio")
        public String ratioGauges() {
            countRatioGauges++;
    
            return String.valueOf(countRatioGauges);
        }
    
        private int countTest = 0;
        private int countRatioGauges = 0;

當/gauges/test請求訪問兩次,/gauges/ratio請求訪問6次 test2指標的輸出結果:

-- Gauges ----------------------------------------------------------------------
test2
             value = 0.75

Gauges 提供一個能夠緩存指定時間的監控CachedGauge,以下是CachedGauge的例子:

    // 註冊一個Cached Gauge
    @Bean
    CachedGauge<Long> cachedGauge(MetricRegistry registry) {
        CachedGauge<Long> cachedGauge = new CachedGauge<Long>(10000, TimeUnit.MILLISECONDS) {
            @Override
            protected Long loadValue() {
                try {
                    System.out.println("start CachedGauge before execute loadValue " + LocalDateTime.now());
                    // 獲取具體的內容
                    long value = longTimeValue();
                    System.out.println("start CachedGauge after execute loadValue " + LocalDateTime.now());
                    return value;
                } catch (InterruptedException e) {
                    System.out.println(e);
                    return 0L;
                }
            }
        };
        registry.register("cached", cachedGauge);
        return cachedGauge;
    }
    
    /**
     * 睡眠2秒後返回一個隨機數
     */
    private long longTimeValue() throws InterruptedException {
        Thread.sleep(2000);
        return random.nextInt(100);
    }
    
    private Random random = new Random();
    
    /**
     * 隨機數生成器
     */
    private Random random;

Cached Guage可以在第一次獲取到結果後,將結果緩存。在緩存未失效前,如果需要獲取結果則直接返回緩存中的結果;如果緩存失效則再次執行獲取結果的任務,並緩存結果。

Gauges提供了一種可以自定義轉換監控數據的監控類型DerivativeGauge,一下是DerivativeGauge使用例子:

    // 定義DerivativeGauge
    
    public GaugesController(MetricRegistry registry) {
    
        // 定義一個Gauge,用於統計接口gauges/test的訪問次數
        Gauge<Long> gauge = () -> (long)countTest;
    
        // 初始化DerivativeGauge,自定義統計接口訪問次數的指標如何轉成成其他指標
        DerivativeGauge<Long, String> derivativeGauge = new DerivativeGauge<Long, String>(gauge) {
            @Override
            protected String transform(Long value) {
                return String.format("這是訪問第%s", value);
            }
        };
    
        registry.register("derivative", derivativeGauge);
    }
    
    @GetMapping(value = "/gauges/test")
    public String gauges() {
        // 非線程安全,只作爲一個演示例子
        countTest++;
        return String.valueOf(countTest);
    }
    
    // 記錄接口訪問次數
    private int countTest = 0;

以下是控制檯輸出:

```
– Gauges -----------------------------------------------------------------–—
derivative
value = 這是訪問第3

```

Counter

Counter是一個計算器監控類型,基於LongAdder實現的計數,以下是使用的例子:

    @RestController
    public class CounterController {
    
    public CounterController(MetricRegistry registry) {
        //初始化一個Counter類型的監控
        counter = new Counter();
        registry.register("counter", counter);
    }
    
    @GetMapping("counter/add")
    public long add() {
        // 每次方法調用時自增一
        counter.inc();
        return counter.getCount();
    }
    
    @GetMapping("counter/minus")
    public long method1() {
        // 方法調用時自減一
        counter.dec();
        return counter.getCount();
    }
    
    /**
     * 計算器
     */
    private Counter counter;

counter/add 請求調用5次,counter/minus請求調用12次後監控輸出如下

-- Counters --------------------------------------------------------------------
counter
             count = -7

Histograms

Histogram是用來統計一個監控指標在連續的時間緯度上的分佈情況的直方圖監控類型,監控的包括:次數,最小值,最大值,均值,標準差,中位數,75%、95%、98%、99%、99.9%分位值。Histogram只適用於小數據量下的計算。

Histogram在數據修改時,通過採樣將數據維護在一個小的、可管理的、在統計上代表整個數據樣本的存儲庫上,通過計算存儲庫上的數據得到統計結果。

使用例子如下:

    
    @RestController
    public class HistogramController {
    
        public HistogramController(MetricRegistry registry) {
            longAdder = new LongAdder();
    
            histogram = registry.histogram(name(HistogramController.class, "counts"));
        }
    
        @GetMapping("histogram")
        public long histogram() {
            longAdder.increment();
            long value = longAdder.longValue();
            histogram.update(value);
            return value;
        }
    
        private LongAdder longAdder;
        private Histogram histogram;
    }

Histogram常用於監控接口響應時間,緩存大小等場景。

Meters

Meters用於監控事件發生的頻率,比如每秒接口被調用的次數。一下是Meter的使用例子:

    
    public MeterController(MetricRegistry registry) {
        getRequests = registry.meter(name(MeterController.class, "meter-requests", "requests"));
    }
    
    @GetMapping("/meter/1")
    public String meter() {
        getRequests.mark();
        return null;
    }
    
    @GetMapping("/meter/2")
    public String meter2() {
        getRequests.mark();
        return null;
    }
    
    @GetMapping("/meter/3")
    public String meter3() {
        getRequests.mark();
        return null;
    }

快速訪問接口/meter/1,日誌輸出如下:

-- Meters ----------------------------------------------------------------------
com.jackframe.practice.metrics.meter.MeterController.meter-requests.requests
             count = 17
         mean rate = 1.70 events/second
     1-minute rate = 1.28 events/second
     5-minute rate = 1.22 events/second
    15-minute rate = 1.21 events/second

Meters監控的是事件的觸發次數,每秒鐘觸發的中位數,1分鐘內/5分鐘內/15分鐘內每秒觸發的次數,經常用於監控接口訪問情況

Timers

Timers是Meters和Histogram的組合,使用如下:

    
    public TimerController(MetricRegistry registry) {
        timer = registry.timer(name(TimerController.class, "get-requests"));
    }
    
    @GetMapping("timer")
    public String timer() {
        final Timer.Context context = timer.time();
        try {
            System.out.println("execute task");
        } finally {
            context.stop();
        }
        return "";
    }
    
    private final Timer timer;

快速訪問/timer接口17次後,監控輸入如下:

-- Timers ----------------------------------------------------------------------
com.jackframe.practice.metrics.timer.TimerController.get-requests
             count = 17
         mean rate = 1.70 calls/second
     1-minute rate = 0.61 calls/second
     5-minute rate = 0.44 calls/second
    15-minute rate = 0.41 calls/second
               min = 0.03 milliseconds
               max = 0.15 milliseconds
              mean = 0.04 milliseconds
            stddev = 0.03 milliseconds
            median = 0.03 milliseconds
              75% <= 0.03 milliseconds
              95% <= 0.15 milliseconds
              98% <= 0.15 milliseconds
              99% <= 0.15 milliseconds
            99.9% <= 0.15 milliseconds 

監控輸出方式

Metrics監控結果輸出方式支持輸出控制檯,SLF4J(日誌文件),CSV(文件)。

以下是定一個每10秒一次將監控數據輸出到控制檯的輸出配置:

    // 註冊一個控制檯輸出工具
    ConsoleReporter reporter = ConsoleReporter.forRegistry(metricRegistry())
                .convertRatesTo(TimeUnit.SECONDS) // 輸出比率的時間單位
                .convertDurationsTo(TimeUnit.MILLISECONDS) // 輸出頻率的時間單位
                .build();
        reporter.start(10, TimeUnit.SECONDS); // 定義每10秒執行一次輸出
        return reporter;

生產環境上的此類信息一般會入日誌系統,Metrics提供將監控輸出到日誌的方式。一下時輸出到日誌的配置:

    
    final CsvReporter reporter = CsvReporter.forRegistry(registry)
                                            .formatFor(Locale.US)
                                            .convertRatesTo(TimeUnit.SECONDS)
                                            .convertDurationsTo(TimeUnit.MILLISECONDS)
                                            .build(new File("~/projects/data/"));
    reporter.start(1, TimeUnit.SECONDS);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章