【第三十五章】 metrics(3)- codahale-metrics基本使用
<!-- metrics --> <dependency> <groupId>io.dropwizard.metrics</groupId> <artifactId>metrics-core</artifactId> </dependency>
这里,依托于springboot,版本号是3.1.2
一、meter类metrics
作用:统计最近1分钟(m1),5分钟(m5),15分钟(m15),还有全部时间的速率(速率就是平均值)
例如:qps
线程安全:mark()方法中的四个操作都是基于CAS实现,统计线程安全。
package com.xxx.secondboot.metrics; import java.util.concurrent.TimeUnit; import com.codahale.metrics.ConsoleReporter; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; /** * Meter * 作用:度量速率(例如,tps) * Meters会统计最近1分钟(m1),5分钟(m5),15分钟(m15),还有全部时间的速率(速率就是平均值)。 */ public class TestMeter { public static void main(String[] args) throws InterruptedException { final MetricRegistry registry = new MetricRegistry();//其实就是一个metrics容器,因为该类的一个属性final ConcurrentMap<String, Metric> metrics,在实际使用中做成单例就好 ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(1, TimeUnit.SECONDS);//从启动后的1s后开始(所以通常第一个计数都是不准的,从第二个开始会越来越准),每隔一秒从MetricRegistry钟poll一次数据 Meter meterTps = registry.meter(MetricRegistry.name(TestMeter.class, "request", "tps"));//将该Meter类型的指定name的metric加入到MetricsRegistry中去 System.out.println("执行与业务逻辑"); while(true){ meterTps.mark();//总数以及m1,m5,m15的数据都+1 Thread.sleep(500); } } }
注意:
- 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()方法源码:
public void start(long period, TimeUnit unit) { executor.scheduleAtFixedRate(new Runnable() { @Override public void run() { try { report(); } catch (RuntimeException ex) { LOG.error("RuntimeException thrown from {}#report. Exception was suppressed.", ScheduledReporter.this.getClass().getSimpleName(), ex); } } }, period, period, unit); }
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
线程安全:只是做读操作,线程安全
package com.xxx.secondboot.metrics; import java.time.LocalDateTime; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.TimeUnit; import com.codahale.metrics.ConsoleReporter; import com.codahale.metrics.Gauge; import com.codahale.metrics.MetricRegistry; /** * Gauge * 作用:只返回一个简单值(一个瞬时值) * eg:返回队列的size */ public class TestGauge { public static Queue<String> queue = new LinkedList<>();//队列 public static void main(String[] args) { MetricRegistry registry = new MetricRegistry(); ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(1, TimeUnit.SECONDS); registry.register(MetricRegistry.name(TestGauge.class, "queue", "size"), new Gauge<Integer>() { public Integer getValue() { return queue.size(); } }); while (true) { try { Thread.sleep(1000); queue.add("job - " + LocalDateTime.now()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
注意:
- 在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,线程安全
package com.xxx.secondboot.metrics; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import com.codahale.metrics.ConsoleReporter; import com.codahale.metrics.Counter; import com.codahale.metrics.MetricRegistry; /** * counter: * 作用:计数器(用gauge封装了AtomicLong) */ public class TestCounters { public static Queue<String> queue = new LinkedBlockingQueue<>(); public static Counter counter;//计算queue的大小 public static void main(String[] args) throws InterruptedException { MetricRegistry registry = new MetricRegistry(); ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(1, TimeUnit.SECONDS); counter = registry.counter(MetricRegistry.name(TestCounters.class, "queue", "size")); int num = 0; while (true) { if (num < 10) { queue.add("job - " + num); counter.inc(); } else if (num > 10 && num < 16) { queue.poll(); counter.dec(); } else { queue.add("job - " + num); counter.inc(); } num++; Thread.sleep(500); } } }
输出:
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的操作需要获取锁,操作之后释放锁。线程安全。
package com.xxx.secondboot.metrics; import java.util.Random; import java.util.concurrent.TimeUnit; import com.codahale.metrics.ConsoleReporter; import com.codahale.metrics.ExponentiallyDecayingReservoir; import com.codahale.metrics.Histogram; import com.codahale.metrics.MetricRegistry; /** * Histogram * 作用:计算执行次数count、最小值min,最大值max,平均值mean,方差stddev,中位数median,75百分位, 90百分位, 95百分位, 98百分位, 99百分位, 和 99.9百分位的值 */ public class TestHistogram { public static void main(String[] args) throws InterruptedException { MetricRegistry registry = new MetricRegistry(); ConsoleReporter reporter = ConsoleReporter.forRegistry(registry) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .build(); reporter.start(1, TimeUnit.SECONDS); Histogram histogram = new Histogram(new ExponentiallyDecayingReservoir());//95% registry.register(MetricRegistry.name(TestHistogram.class, "request","histogram"), histogram); Random random = new Random(); while(true){ Thread.sleep(1000); histogram.update(random.nextInt(10000)); } } }
16-10-3 21:26:05 =============================================================== -- Histograms ------------------------------------------------------------------ com.xxx.secondboot.metrics.TestHistogram.request.histogram count = 3 min = 685 max = 6754 mean = 3149.05 stddev = 2584.36 median = 2078.00 75% <= 6754.00 95% <= 6754.00 98% <= 6754.00 99% <= 6754.00 99.9% <= 6754.00
五、timer类metrics
作用:meter和histogram的组合体
例如:统计某个函数的qps和执行耗时。
线程安全:meter和histogram都安全,所以也线程安全
package com.xxx.secondboot.metrics; import java.util.concurrent.TimeUnit; import com.codahale.metrics.ConsoleReporter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; /** * Timers * 作用:histogram和meter的组合体 */ public class TestTimer { public static void main(String[] args) throws InterruptedException { MetricRegistry registry = new MetricRegistry(); ConsoleReporter reporter = ConsoleReporter.forRegistry(registry).build(); reporter.start(1, TimeUnit.SECONDS); Timer timer = registry.timer(MetricRegistry.name(TestTimer.class, "get-latency")); Timer.Context ctx = timer.time(); try{ Thread.sleep(2000); }finally{ ctx.stop(); } } }
输出:
-- Timers ---------------------------------------------------------------------- com.xxx.secondboot.metrics.TestTimer.get-latency count = 0 mean rate = 0.00 calls/second 1-minute rate = 0.00 calls/second 5-minute rate = 0.00 calls/second 15-minute rate = 0.00 calls/second min = 0.00 milliseconds max = 0.00 milliseconds mean = 0.00 milliseconds stddev = 0.00 milliseconds median = 0.00 milliseconds 75% <= 0.00 milliseconds 95% <= 0.00 milliseconds 98% <= 0.00 milliseconds 99% <= 0.00 milliseconds 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/
最新文章
- SQL递归查询(with cte as)
- 迭代输出Map和List<;Map<;String,Object>;>;的方法
- H264码流打包分析(精华)
- 微服务、SOA 和 API:是敌是友?
- javascript 高级程序设计(三)-数据类型
- 转:PHP开发框架流行度排名:Laravel居首
- 3d touch 应用 2 -备用
- LVM命令摘要
- animation之translate、scale、alpha、rotate动画效果呈现
- AngularJs学习(1)
- iOS 设置UIDatePiicer为24小时制
- hdu 4961 Boring Sum(数学题)
- C# 使用Tuple传递多个参数
- 深入理解计算机系统(2.3)------布尔代数以及C语言运算符
- 基于BCGP库的界面效果
- Linux系统启动过程(通俗易懂)
- 前端导出csv
- thinkphp5省市区三级联动例子
- python random使用方法
- Mac OS X 启用超级用户 sudo -s 获得系统权限 Mac终端命令
热门文章
- 【python-opencv】17-形态学操作-腐蚀与膨胀
- oracle(九)索引扫描
- Codeforces Round #247 (Div. 2) D. Random Task
- 什么是 Delta 文件
- (转)spring boot整合redis
- ComBSTR的使用
- FM/FFM原理
- 2018-2019-2 网络对抗技术 20165324 Exp1:PC平台逆向破解
- 非线性方程(组):MATLAB内置函数 solve, vpasolve, fsolve, fzero, roots [MATLAB]
- 消息 8101,级别 16,状态 1,第 1 行 仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表&#39;ResourceInfo&#39;中的标识列指定显式值。