Histogram用来统计数据的分布。Histogram可以提供收集到的数据的最大值、最小值、平均值和中值,此外还能提供百分比分布,如75%,95%,99.9%等等。
Histogram是我学习Metrics的驱动之一。最初是想使用Histogram来优化接口处理统计能力。
看下下面的类图:

类图表示了Histogram需要用到的几类和个接口之间的关系,简单说明下:
- Sampling接口:意思是取样器,只有一个方法,作用是取出某一阶段的统计结果快照(Snapshot);
- Reservoir接口:数据池,所有的记录最终都会写入Reservoir实例并完成运算;Metrics提供了多种数据池来执行不同的抽样运算;
- Snapshot接口:快照接口,作用是对Reservoir中的数据进行二次计算并生成统计结果;Snapshot提供了统计数据的最大值、最小值、标准差、平均值、中值、95分位值等指标;
- Histogram类:实现了Sampling接口,是对外交互的入口。
老规矩,看一段示例代码:
public class HistogramShow {
public static void main(String[] args) throws InterruptedException {
final MetricRegistry metrics = new MetricRegistry();
final ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();
reporter.start(3, TimeUnit.SECONDS);
Histogram histogram = new Histogram(new ExponentiallyDecayingReservoir());
metrics.register("方法执行时长统计", histogram);
for (int i = 0; i < 100; i++) {
long start = System.currentTimeMillis();
try {
delayedMethod();
} finally {
histogram.update(System.currentTimeMillis() - start);
}
}
}
private static void delayedMethod() throws InterruptedException {
long time = (long) (Math.random() * 1000);
System.out.println("------>>method used time: " + time);
TimeUnit.MILLISECONDS.sleep(time);
}
}
这段代码中定义了一个delayedMethod()方法,该方法会随机sleep一段时间来模拟方法执行时长。代码主体就是使用Histogram报表来统计每三秒钟内这个方法的执行状态。
MetricRegistry也提供了Histogram实例的创建注册方法,不过为这里了更直观一些,还是使用了直接new关键字来构建Histogram实例。可以看到,每次创建Histogram对象都需要传入一个Reservoir接口的实例。
看下执行结果片段:
------>>method used time: 522
------>>method used time: 537
------>>method used time: 820
------>>method used time: 669
19-8-24 16:59:41 ===============================================================
-- Histograms ------------------------------------------------------------------
方法执行时长统计
count = 3
min = 534
max = 820
mean = 632.10
stddev = 134.38
median = 538.00
75% <= 820.00
95% <= 820.00
98% <= 820.00
99% <= 820.00
99.9% <= 820.00
统计结果即是由Snapshot提供。
简单介绍下metrics提供的几种Reservoir:
UniformReservoir:默认保存1028条记录,每次进行update操作的时候,首先会依次地将值填入1028条记录中,当记录满了之后,就会使用随机替换0 – 1027中的一条(随机抽样1028条记录)。因为是随机替换,所以也不需要进行加锁和解锁。SlidingWindowReservoir:固定大小的数据池,从0到n-1填入数据,但是不会对数据进行更新,也不会进行加锁和解锁(固定抽样n条记录)。SlidingTimeWindowReservoir:非固定大小的数据池,但是只会存储过去N秒的数据(抽样N秒内的记录)。使用ConcurrentSkipListMap进行存储。ExponentiallyDecayingReservoir:固定大小的数据池。首先会逐个数据填满数据池,随后会将老的数据替换为新的数据(抽样n条最新的记录),使用ConcurrentSkipListMap进行存储。可以说是SlidingWindowReservoir与SlidingTimeWindowReservoir的结合。
就这样。
发表评论