JAVA监控之Metrics

1. Metrics是什么?

它是一个工具集,不是一个框架。它在kafka,spark,hadoop,flink和cassandra等流行框架中都得到了广泛的应用。

官网:https://metrics.dropwizard.io

2. Metrics之Meter

meter用于统计基于时间的时间率(event rate over time),主要包含:

(1)监控开始至当前总共接收的时间数量

(2)估算的平均事件率

(3)估算的1min内的事件率

(4)估算的5min内的事件率

(5)估算的15min内的事件率

2.1 代码示例

(1)pom依赖


    io.dropwizard.metrics
    metrics-core
    3.2.6

(2)代码

public class MeterMetricsDemo {
    //metrics注册中心
    private static final MetricRegistry metrics = new MetricRegistry();
    //创建meter类型的监控,并注册到注册中心,name会作为统计结果的标题,它用于统计请求事件率
    /**
     * -- Meters ----------------------------------------------------------------------
     * reqRate
     */
    private static final Meter reqRate = metrics.meter("reqRate");
    //用于统计请求体的大小
    /**
     * -- Meters ----------------------------------------------------------------------
     * reqSize
     */
    private static final Meter reqSize = metrics.meter("reqSize");

    public static void main(String[] args) {
        startReport();
        while (true) {
            sleepRandomSeconds();
            response(new byte[ThreadLocalRandom.current().nextInt(10_000_000)]);
        }
    }
    private static void response(byte[] request){
        System.out.println("==========接收到请求");
        reqRate.mark();
        reqSize.mark(request.length);
    }

    private static void startReport() {
        ConsoleReporter reporter =
                //给metrics设置报告模式
                ConsoleReporter.forRegistry(metrics)
                //设置事件率的统计事件,例如events/second,events/millisecond, events/hour...
                .convertRatesTo(TimeUnit.SECONDS)
                //Timer类型的metrics才会用到,meter不起作用
                //.convertDurationsTo(TimeUnit.HOURS)
                .build();

        //设置每隔多长时间间隔打印一次报告,例如每隔3s打印一次报告
        reporter.start(3, TimeUnit.SECONDS);
    }

    private static void sleepRandomSeconds() {

        int random = ThreadLocalRandom.current().nextInt(3);
        try {
            TimeUnit.SECONDS.sleep(random);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

(3)日志输出

==========接收到请求
==========接收到请求
==========接收到请求
22-3-31 22:09:46 ===============================================================

-- Meters ---
-------------------------------------------------------------------
reqRate
             count = 4
         mean rate = 1.15 events/second
     1-minute rate = 0.00 events/second
     5-minute rate = 0.00 events/second
    15-minute rate = 0.00 events/second
reqSize
             count = 20404333
         mean rate = 5858540.55 events/second
     1-minute rate = 0.00 events/second
     5-minute rate = 0.00 events/second
    15-minute rate = 0.00 events/second


==========接收到请求
22-3-31 22:09:49 ===============================================================

-- Meters ----------------------------------------------------------------------
reqRate
             count = 5
         mean rate = 0.77 events/second
     1-minute rate = 1.00 events/second
     5-minute rate = 1.00 events/second
    15-minute rate = 1.00 events/second
reqSize
             count = 23931535
         mean rate = 3686812.79 events/second
     1-minute rate = 4786307.00 events/second
     5-minute rate = 4786307.00 events/second
    15-minute rate = 4786307.00 events/second

3. Metrics之Gauge

gauge用户记录某一时刻的value值,例如某时刻queue的size,某一时刻cache的逐出数...

JAVA监控之Metrics_第1张图片

 

3.1 SimpleGauge

示例代码:

public class QueueManager {
    private final Queue queue;

    public QueueManager(MetricRegistry metrics, String name) {
        this.queue = new Queue();
        //每个metric在registry中必需有一个唯一的名字,名字是一个点号分割的字符串
        //例如:com.john.metrics.gauge.QueueManager.mktPriceQueue.size
        metrics.register(MetricRegistry.name(QueueManager.class, name, "size"),
                         (Gauge) queue::size);
    }
}

QueueManager queueManager = new QueueManager(metrics, "mktPriceQueue");
        ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics)
                //Timer和Meter才起作用
                //.convertRatesTo(TimeUnit.SECONDS)
                .build();

日志输出:

22-4-1 9:13:31 =================================================================

-- Gauges ----------------------------------------------------------------------
com.john.metrics.gauge.QueueManager.mktPriceQueue.size
             value = 3


22-4-1 9:13:34 =================================================================

-- Gauges ----------------------------------------------------------------------
com.john.metrics.gauge.QueueManager.mktPriceQueue.size
             value = 14

3.2 JMXGauge

可以使用jmx的数据

代码示例:

public class JmxGaugeDemo {

    private static final MetricRegistry registry = new MetricRegistry();
    private static final ConsoleReporter repoter = ConsoleReporter.forRegistry(registry)
            .convertRatesTo(TimeUnit.SECONDS)
            .convertDurationsTo(TimeUnit.SECONDS)
            .build();

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

        registry.register(MetricRegistry.name(JmxGaugeDemo.class, "HeapMemory"), new JmxAttributeGauge(
                new ObjectName("java.lang:type=Memory"), "HeapMemoryUsage"

        ));
        registry.register(MetricRegistry.name(JmxGaugeDemo.class, "NonHeapMemory"), new JmxAttributeGauge(
                new ObjectName("java.lang:type=Memory"), "NonHeapMemoryUsage"

        ));

        while (true){}
    }

}

日志示例

22-4-2 18:18:02 ================================================================

-- Gauges ----------------------------------------------------------------------
com.john.metrics.gauge.JmxGaugeDemo.HeapMemory
             value = javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=126877696, init=132120576, max=1875378176, used=8088400})
com.john.metrics.gauge.JmxGaugeDemo.NonHeapMemory
             value = javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=((itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)))),contents={committed=10682368, init=2555904, max=-1, used=9314576})

3.3 RatioGauge

代码示例:

public class RatioGaugeDemo {

    private static final MetricRegistry registry = new MetricRegistry();
    private static final ConsoleReporter repoter = ConsoleReporter.forRegistry(registry)
            .convertRatesTo(TimeUnit.SECONDS)
            .convertDurationsTo(TimeUnit.SECONDS)
            .build();

    private static final Meter totalMeter = new Meter();
    private static final Meter successMeter = new Meter();

    public static void main(String[] args) {
        repoter.start(3, TimeUnit.SECONDS);
        registry.register("successRatio", new RatioGauge() {
            @Override
            protected Ratio getRatio() {
                return Ratio.of(successMeter.getCount(), totalMeter.getCount());
            }
        });

        while (true) {
            doBusiness();
            SleepRandomUtil.sleep(2);
        }
    }

    private static void doBusiness(){
        totalMeter.mark();
        try{
            int count = ThreadLocalRandom.current().nextInt(2);
            if(count == 0){
                System.out.println(">>>>>>>>>>>>>>");
            }
            int res = 3 / count;
            successMeter.mark();
        } catch (Exception e) {

        }
    }
}

日志示例: 

22-4-2 18:52:34 ================================================================

-- Gauges ----------------------------------------------------------------------
successRatio
             value = 0.8888888888888888

3.4 CachedGauge

代码示例:

public class CachedGaugeDemo {

    private static final MetricRegistry registry = new MetricRegistry();
    private static final ConsoleReporter repoter = ConsoleReporter.forRegistry(registry)
            .convertRatesTo(TimeUnit.SECONDS)
            .convertDurationsTo(TimeUnit.SECONDS)
            .build();

    public static void main(String[] args) {
        repoter.start(3, TimeUnit.SECONDS);
        //10s中时间内都是从缓存中获取
        registry.register("cachedGauge", new CachedGauge(5, TimeUnit.SECONDS) {
            @Override
            protected Long loadValue() {
                return queryFromDb();
            }
        });
        while (true){}
    }

    private static Long queryFromDb(){
        SleepRandomUtil.sleep(2);
        return Long.valueOf(ThreadLocalRandom.current().nextInt(10_000));

    }
}

日志示例:

22-4-2 19:11:03 ================================================================

-- Gauges ----------------------------------------------------------------------
cachedGauge
             value = 6828


22-4-2 19:11:06 ================================================================

-- Gauges ----------------------------------------------------------------------
cachedGauge
             value = 6828


22-4-2 19:11:09 ================================================================

-- Gauges ----------------------------------------------------------------------
cachedGauge
             value = 3615

3.5 DerivativeGauge

4. Metrics之Counter

counter是一个使用AtomicLong进行存储的计数器,可以增减其值。自身值的增减可以减少对queue API操作而对其性能的影响。

代码示例:

public class SimpleCounter {

    private static final MetricRegistry registry = new MetricRegistry();
    private static final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();
    private static final ArrayBlockingQueue queue = new ArrayBlockingQueue<>(100);
    private static final Counter counter = registry.counter(
            MetricRegistry.name(SimpleCounter.class, "pendingJobs", "size"));

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

        new Thread(() -> {
            while (true) {
                addMsg("hello");
                counter.inc();
                SleepRandomUtil.sleep(2);
            }
        }, "producer").start();

        new Thread(() -> {
            while (true) {
                if(consumerMsg() != null){
                    counter.dec();
                }
                SleepRandomUtil.sleep(10);
            }
        }, "consumer").start();

        while (true) {

        }
    }

    public static void addMsg(String msg) {
        try {
            queue.put(msg);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static String consumerMsg() {
        try {
            String msg = queue.take();
            return msg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

}

日志示例:

-- Counters --------------------------------------------------------------------
com.john.metrics.counter.SimpleCounter.pendingJobs.size
             count = 10


22-4-1 9:52:51 =================================================================

-- Counters --------------------------------------------------------------------
com.john.metrics.counter.SimpleCounter.pendingJobs.size
             count = 13

5. Metrics之Histograms

直方图,用于统计数据流的统计分布。例如,统计搜索词对搜索结果的影响,统计某方法的调用时长...

代码示例:

public class HistogramsDemo {
    private static final MetricRegistry registry = new MetricRegistry();
    private static final Histogram responseSizes = registry.histogram(MetricRegistry.name(HistogramsDemo.class, "response-sizes"));
    private static final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build();

    public static void main(String[] args) {
        reporter.start(3, TimeUnit.SECONDS);
        while (true) {
            response(new byte[ThreadLocalRandom.current().nextInt(10)]);
            SleepRandomUtil.sleep(2);
        }
    }

    private static void response(byte[] request){
        responseSizes.update(request.length);
    }
}

日志示例:

22-4-1 19:13:05 ================================================================

-- Histograms ------------------------------------------------------------------
com.john.metrics.histogram.HistogramsDemo.response-sizes
             count = 5       //调用update的次数
               min = 3       //最小的请求体大小
               max = 8       //最大的请求体大小
              mean = 4.38    //平均值
            stddev = 1.95    //标准方差
            median = 3.00    //中位值
              75% <= 5.00    //75%的请求体大小小于5
              95% <= 8.00    //95%的请求体大小小于8
              98% <= 8.00    //98%的请求体大小小于8
              99% <= 8.00    //99%的请求体大小小于8
            99.9% <= 8.00    //99.9%的请求体大小小于8


22-4-1 19:13:08 ================================================================

-- Histograms ------------------------------------------------------------------
com.john.metrics.histogram.HistogramsDemo.response-sizes
             count = 14
               min = 0
               max = 9
              mean = 4.87
            stddev = 3.09
            median = 5.00
              75% <= 8.00
              95% <= 9.00
              98% <= 9.00
              99% <= 9.00
            99.9% <= 9.00

5.1 median的四种统计方式

6. Metrics之Timer

计时器测量特定代码段被调用的速率及其耗时时间分布。  

代码示例:

public class TimerDemo {

    private static final MetricRegistry registry = new MetricRegistry();
    private static final Timer timer = registry.timer(MetricRegistry.name(TimerDemo.class, "request"), Timer :: new);
    private static final ConsoleReporter reporter = ConsoleReporter.forRegistry(registry)
            //统计每秒时间内调用多少次,例如:calls/second, calls/minute
            .convertRatesTo(TimeUnit.SECONDS)
            //每次调用的耗时统计单位,例如seconds, minutes
            .convertDurationsTo(TimeUnit.SECONDS)
            .build();

    public static void main(String[] args) {
        reporter.start(2, TimeUnit.SECONDS);
        while (true) {
            response(new byte[ThreadLocalRandom.current().nextInt(10)]);
        }
    }

    private static void response(byte[] request){
        Timer.Context context = timer.time();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (Exception e) {

        }finally {
            long stop = context.stop();  //nano seconds
            System.out.println("elapse " + stop);
        }

    }
}

日志示例:

22-4-2 10:35:43 ================================================================

-- Timers ------------------------------------------------------------------
com.john.metrics.timer.TimerDemo.request
             count = 12                    //到目前位置调用了12次
         mean rate = 0.50 calls/second     //平均每秒调用0.5次
     1-minute rate = 0.41 calls/second     //1min窗口内的统计结果:每秒调用0.5次
     5-minute rate = 0.40 calls/second     //5min窗口内的统计结果:每秒调用0.5次
    15-minute rate = 0.40 calls/second     //15min窗口内的统计结果:每秒调用0.5次
               min = 2.00 seconds          //最小调用耗时2s
               max = 2.00 seconds          //最大调用耗时2s
              mean = 2.00 seconds          //平均调用耗时2s
            stddev = 0.00 seconds          //标准差0
            median = 2.00 seconds          //中位数2s
              75% <= 2.00 seconds          //75%的调用耗时小于等于2s
              95% <= 2.00 seconds          //95%的调用耗时小于等于2s
              98% <= 2.00 seconds          //98%的调用耗时小于等于2s
              99% <= 2.00 seconds          //99%的调用耗时小于等于2s
            99.9% <= 2.00 seconds          //99.9%的调用耗时小于等于2s

7. Metrics之Repoter

你可能感兴趣的:(java)