java版的Metric工具介紹

Metrics是一個給JAVA服務的各項指標提供度量工具的包,在JAVA代碼中嵌入Metrics代碼,可以方便的對業務代碼的各個指標進行監控,同時,Metrics能夠很好的跟Ganlia、Graphite結合,方便的提供圖形化接口。基本使用方式直接將core包(目前穩定版本3.0.1)導入pom文件即可,配置如下:
<dependency> <groupId>com.codahale.metricsgroupId> <artifactId>metrics-coreartifactId> <version>3.0.1version> dependency>

core包主要提供如下核心功能:

  • Metrics Registries類似一個metrics容器,維護一個Map,可以是一個服務一個實例。
  • 支持五種metric類型:Gauges、Counters、Meters、Histograms和Timers。
  • 可以將metrics值通過JMX、Console,CSV文件和SLF4J loggers發佈出來。

五種Metrics類型:

1.       Gauges

Gauges是一個最簡單的計量,一般用來統計瞬時狀態的數據信息,比如系統中處於pending狀態的job。測試代碼

    package com.netease.test.metrics;

    import com.codahale.metrics.ConsoleReporter;
    import com.codahale.metrics.Gauge;
    import com.codahale.metrics.JmxReporter;
    import com.codahale.metrics.MetricRegistry;

    import java.util.Queue;
    import java.util.concurrent.LinkedBlockingDeque;
    import java.util.concurrent.TimeUnit;

    /**
     * User: hzwangxx
     * Date: 14-2-17
     * Time: 14:47
     * 測試Gauges,實時統計pending狀態的job個數
     */
    public class TestGauges {
        /**
         * 實例化一個registry,最核心的一個模塊,相當於一個應用程序的metrics系統的容器,維護一個Map
         */
        private static final MetricRegistry metrics = new MetricRegistry();

        private static Queue<String> queue = new LinkedBlockingDeque<String>();

        /**
         * 在控制檯上打印輸出
         */
        private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();

        public static void main(String[] args) throws InterruptedException {
            reporter.start(3, TimeUnit.SECONDS);

            //實例化一個Gauge
            Gauge<Integer> gauge = new Gauge<Integer>() {
                @Override
                public Integer getValue() {
                    return queue.size();
                }
            };

            //註冊到容器中
            metrics.register(MetricRegistry.name(TestGauges.class, "pending-job", "size"), gauge);

            //測試JMX
            JmxReporter jmxReporter = JmxReporter.forRegistry(metrics).build();
            jmxReporter.start();

            //模擬數據
            for (int i=0; i<20; i++){
                queue.add("a");
                Thread.sleep(1000);
            }

        }
    }

    /*
    console output:
    14-2-17 15:29:35 ===============================================================

    -- Gauges ----------------------------------------------------------------------
    com.netease.test.metrics.TestGauges.pending-job.size
                 value = 4


    14-2-17 15:29:38 ===============================================================

    -- Gauges ----------------------------------------------------------------------
    com.netease.test.metrics.TestGauges.pending-job.size
                 value = 6


    14-2-17 15:29:41 ===============================================================

    -- Gauges ----------------------------------------------------------------------
    com.netease.test.metrics.TestGauges.pending-job.size
                 value = 9
     */

通過以上步驟將會向MetricsRegistry容器中註冊一個名字爲com.netease.test.metrics .TestGauges.pending-job.size的metrics,實時獲取隊列長度的指標。另外,Core包種還擴展了幾種特定的Gauge:
  • JMX Gauges—提供給第三方庫只通過JMX將指標暴露出來。
  • Ratio Gauges—簡單地通過創建一個gauge計算兩個數的比值。
  • Cached Gauges—對某些計量指標提供緩存

Derivative Gauges—提供Gauge的值是基於其他Gauge值的接口。

2.       Counter

Counter是Gauge的一個特例,維護一個計數器,可以通過inc()和dec()方法對計數器做修改。使用步驟與Gauge基本類似,在MetricRegistry中提供了靜態方法可以直接實例化一個Counter。

    package com.netease.test.metrics;

    import com.codahale.metrics.ConsoleReporter;
    import com.codahale.metrics.Counter;
    import com.codahale.metrics.MetricRegistry;

    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.concurrent.TimeUnit;
    import static com.codahale.metrics.MetricRegistry.*;
    /**
     * User: hzwangxx
     * Date: 14-2-14
     * Time: 14:02
     * 測試Counter
     */
    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();
         * metrics.register(MetricRegistry.name(TestCounter.class, "pending-jobs"), pendingJobs);
         */
        private static Counter pendingJobs = metrics.counter(name(TestCounter.class, "pedding-jobs"));
    // 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 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);
            }

        }
    }

    /*
    console output:
    14-2-17 17:52:34 ===============================================================

    -- Counters --------------------------------------------------------------------
    com.netease.test.metrics.TestCounter.pedding-jobs
                 count = 4


    14-2-17 17:52:37 ===============================================================

    -- Counters --------------------------------------------------------------------
    com.netease.test.metrics.TestCounter.pedding-jobs
                 count = 6


    14-2-17 17:52:40 ===============================================================

    -- Counters --------------------------------------------------------------------
    com.netease.test.metrics.TestCounter.pedding-jobs
                 count = 9

     */


3.       Meters

Meters用來度量某個時間段的平均處理次數(request per second),每1、5、15分鐘的TPS。比如一個service的請求數,通過metrics.meter()實例化一個Meter之後,然後通過meter.mark()方法就能將本次請求記錄下來。統計結果有總的請求數,平均每秒的請求數,以及最近的1、5、15分鐘的平均TPS。

    package com.netease.test.metrics;

    import com.codahale.metrics.ConsoleReporter;
    import com.codahale.metrics.Meter;
    import com.codahale.metrics.MetricRegistry;

    import java.util.concurrent.TimeUnit;

    import static com.codahale.metrics.MetricRegistry.*;

    /**
     * User: hzwangxx
     * Date: 14-2-17
     * Time: 18:34
     * 測試Meters
     */
    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(name(TestMeters.class, "request"));

        public static void handleRequest() {
            requests.mark();
        }

        public static void main(String[] args) throws InterruptedException {
            reporter.start(3, TimeUnit.SECONDS);
            while(true){
                handleRequest();
                Thread.sleep(100);
            }
        }

    }

    /*
    14-2-17 18:43:08 ===============================================================

    -- Meters ----------------------------------------------------------------------
    com.netease.test.metrics.TestMeters.request
                 count = 30
             mean rate = 9.95 events/second
         1-minute rate = 0.00 events/second
         5-minute rate = 0.00 events/second
        15-minute rate = 0.00 events/second


    14-2-17 18:43:11 ===============================================================

    -- Meters ----------------------------------------------------------------------
    com.netease.test.metrics.TestMeters.request
                 count = 60
             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


    14-2-17 18:43:14 ===============================================================

    -- Meters ----------------------------------------------------------------------
    com.netease.test.metrics.TestMeters.request
                 count = 90
             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%)。例如,需要統計某個頁面的請求響應時間分佈情況,可以使用該種類型的Metrics進行統計。具體的樣例代碼如下:

    package com.netease.test.metrics;

    import com.codahale.metrics.ConsoleReporter;
    import com.codahale.metrics.Histogram;
    import com.codahale.metrics.MetricRegistry;

    import java.util.Random;
    import java.util.concurrent.TimeUnit;

    import static com.codahale.metrics.MetricRegistry.name;

    /**
     * User: hzwangxx
     * Date: 14-2-17
     * Time: 18:34
     * 測試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 randomNums = metrics.histogram(name(TestHistograms.class, "random"));

        public static void handleRequest(double random) {
            randomNums.update((int) (random*100));
        }

        public static void main(String[] args) throws InterruptedException {
            reporter.start(3, TimeUnit.SECONDS);
            Random rand = new Random();
            while(true){
                handleRequest(rand.nextDouble());
                Thread.sleep(100);
            }
        }

    }

    /*
    14-2-17 19:39:11 ===============================================================

    -- Histograms ------------------------------------------------------------------
    com.netease.test.metrics.TestHistograms.random
                 count = 30
                   min = 1
                   max = 97
                  mean = 45.93
                stddev = 29.12
                median = 39.50
                  75% <= 71.00
                  95% <= 95.90
                  98% <= 97.00
                  99% <= 97.00
                99.9% <= 97.00


    14-2-17 19:39:14 ===============================================================

    -- Histograms ------------------------------------------------------------------
    com.netease.test.metrics.TestHistograms.random
                 count = 60
                   min = 0
                   max = 97
                  mean = 41.17
                stddev = 28.60
                median = 34.50
                  75% <= 69.75
                  95% <= 92.90
                  98% <= 96.56
                  99% <= 97.00
                99.9% <= 97.00


    14-2-17 19:39:17 ===============================================================

    -- Histograms ------------------------------------------------------------------
    com.netease.test.metrics.TestHistograms.random
                 count = 90
                   min = 0
                   max = 97
                  mean = 44.67
                stddev = 28.47
                median = 43.00
                  75% <= 71.00
                  95% <= 91.90
                  98% <= 96.18
                  99% <= 97.00
                99.9% <= 97.00
    */

5.       Timers

Timers主要是用來統計某一塊代碼段的執行時間以及其分佈情況,具體是基於Histograms和Meters來實現的。樣例代碼如下:

    package com.netease.test.metrics;

    import com.codahale.metrics.ConsoleReporter;
    import com.codahale.metrics.MetricRegistry;
    import com.codahale.metrics.Timer;

    import java.util.Random;
    import java.util.concurrent.TimeUnit;

    import static com.codahale.metrics.MetricRegistry.name;

    /**
     * User: hzwangxx
     * Date: 14-2-17
     * Time: 18:34
     * 測試Timers
     */
    public class TestTimers {
        /**
         * 實例化一個registry,最核心的一個模塊,相當於一個應用程序的metrics系統的容器,維護一個Map
         */
        private static final MetricRegistry metrics = new MetricRegistry();

        /**
         * 在控制檯上打印輸出
         */
        private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();

        /**
         * 實例化一個Meter
         */
    // private static final Timer requests = metrics.timer(name(TestTimers.class, "request"));
        private static final Timer requests = metrics.timer(name(TestTimers.class, "request"));

        public static void handleRequest(int sleep) {
            Timer.Context context = requests.time();
            try {
                //some operator
                Thread.sleep(sleep);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                context.stop();
            }

        }

        public static void main(String[] args) throws InterruptedException {
            reporter.start(3, TimeUnit.SECONDS);
            Random random = new Random();
            while(true){
                handleRequest(random.nextInt(1000));
            }
        }

    }

    /*
    14-2-18 9:31:54 ================================================================

    -- Timers ----------------------------------------------------------------------
    com.netease.test.metrics.TestTimers.request
                 count = 4
             mean rate = 1.33 calls/second
         1-minute rate = 0.00 calls/second
         5-minute rate = 0.00 calls/second
        15-minute rate = 0.00 calls/second
                   min = 483.07 milliseconds
                   max = 901.92 milliseconds
                  mean = 612.64 milliseconds
                stddev = 196.32 milliseconds
                median = 532.79 milliseconds
                  75% <= 818.31 milliseconds
                  95% <= 901.92 milliseconds
                  98% <= 901.92 milliseconds
                  99% <= 901.92 milliseconds
                99.9% <= 901.92 milliseconds


    14-2-18 9:31:57 ================================================================

    -- Timers ----------------------------------------------------------------------
    com.netease.test.metrics.TestTimers.request
                 count = 8
             mean rate = 1.33 calls/second
         1-minute rate = 1.40 calls/second
         5-minute rate = 1.40 calls/second
        15-minute rate = 1.40 calls/second
                   min = 41.07 milliseconds
                   max = 968.19 milliseconds
                  mean = 639.50 milliseconds
                stddev = 306.12 milliseconds
                median = 692.77 milliseconds
                  75% <= 885.96 milliseconds
                  95% <= 968.19 milliseconds
                  98% <= 968.19 milliseconds
                  99% <= 968.19 milliseconds
                99.9% <= 968.19 milliseconds


    14-2-18 9:32:00 ================================================================

    -- Timers ----------------------------------------------------------------------
    com.netease.test.metrics.TestTimers.request
                 count = 15
             mean rate = 1.67 calls/second
         1-minute rate = 1.40 calls/second
         5-minute rate = 1.40 calls/second
        15-minute rate = 1.40 calls/second
                   min = 41.07 milliseconds
                   max = 968.19 milliseconds
                  mean = 591.35 milliseconds
                stddev = 302.96 milliseconds
                median = 650.56 milliseconds
                  75% <= 838.07 milliseconds
                  95% <= 968.19 milliseconds
                  98% <= 968.19 milliseconds
                  99% <= 968.19 milliseconds
                99.9% <= 968.19 milliseconds

    */


6  Health Checks

Metrics提供了一個獨立的模塊:Health Checks,用於對Application、其子模塊或者關聯模塊的運行是否正常做檢測。該模塊是獨立metrics-core模塊的,使用時則導入metrics-healthchecks包。

<dependency> <groupId>com.codahale.metricsgroupId> <artifactId>metrics-healthchecksartifactId> <version>3.0.1version> dependency>

使用起來和與上述幾種類型的Metrics有點類似,但是需要重新實例化一個Metrics容器HealthCheckRegistry,待檢測模塊繼承抽象類HealthCheck並實現check()方法即可,然後將該模塊註冊到HealthCheckRegistry中,判斷的時候通過isHealthy()接口即可。如下示例代碼:

    package com.netease.test.metrics;

    import com.codahale.metrics.health.HealthCheck;
    import com.codahale.metrics.health.HealthCheckRegistry;

    import java.util.Map;
    import java.util.Random;

    /**
     * User: hzwangxx
     * Date: 14-2-18
     * Time: 9:57
     */
    public class DatabaseHealthCheck extends HealthCheck{
        private final Database database;

        public DatabaseHealthCheck(Database database) {
            this.database = database;
        }

        @Override
        protected Result check() throws Exception {
            if (database.ping()) {
                return Result.healthy();
            }
            return Result.unhealthy("Can't ping database.");
        }

        /**
         * 模擬Database對象
         */
        static class Database {
            /**
             * 模擬database的ping方法
             * @return 隨機返回boolean值
             */
            public boolean ping() {
                Random random = new Random();
                return random.nextBoolean();
            }
        }

        public static void main(String[] args) {
    // MetricRegistry metrics = new MetricRegistry();
    // ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();
            HealthCheckRegistry registry = new HealthCheckRegistry();
            registry.register("database1", new DatabaseHealthCheck(new Database()));
            registry.register("database2", new DatabaseHealthCheck(new Database()));
            while (true) {
                for (Map.Entry<String, Result> entry : registry.runHealthChecks().entrySet()) {
                    if (entry.getValue().isHealthy()) {
                        System.out.println(entry.getKey() + ": OK");
                    } else {
                        System.err.println(entry.getKey() + ": FAIL, error message: " + entry.getValue().getMessage());
                        final Throwable e = entry.getValue().getError();
                        if (e != null) {
                            e.printStackTrace();
                        }
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {

                }
            }
        }
    }

    /*
    console output:
    database1: OK
    database2: FAIL, error message: Can't ping database.
    database1: FAIL, error message: Can't ping database.
    database2: OK
    database1: OK
    database2: FAIL, error message: Can't ping database.
    database1: FAIL, error message: Can't ping database.
    database2: OK
    database1: FAIL, error message: Can't ping database.
    database2: FAIL, error message: Can't ping database.
    database1: FAIL, error message: Can't ping database.
    database2: FAIL, error message: Can't ping database.
    database1: OK
    database2: OK
    database1: OK
    database2: FAIL, error message: Can't ping database.
    database1: FAIL, error message: Can't ping database.
    database2: OK
    database1: OK
    database2: OK
    database1: FAIL, error message: Can't ping database.
    database2: OK
    database1: OK
    database2: OK
    database1: OK
    database2: OK
    database1: OK
    database2: FAIL, error message: Can't ping database.
    database1: FAIL, error message: Can't ping database.
    database2: FAIL, error message: Can't ping database.

     */


其他支持

metrics提供了對Ehcache、Apache HttpClient、JDBI、Jersey、Jetty、Log4J、Logback、JVM等的集成,可以方便地將Metrics輸出到Ganglia、Graphite中,供用戶圖形化展示。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章