/******************************************************************************* * Copyright (c) 2005, 2014 springside.github.io * * Licensed under the Apache License, Version 2.0 (the "License"); *******************************************************************************/ package org.springside.modules.metrics.metric; import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Histogram数据类型, 主要用于计算Latency. * * 报告Report间隔时间内数值的最小/最大,平均值,以及某个百分比的请求数都小于的值. */ public class Histogram { public HistogramMetric latestMetric;// Snapshot值 private Double[] pcts; // 配置需要计算的百分位,如99, 99.99 private int sampleRate; // 采样率,1代表100%, 2代表50%, 10 代表10% private AtomicInteger sampleCounter = new AtomicInteger(0); // 采样率取模用的计数器 private volatile LinkedList<Long> measurements; // 统计周期内的原始数据 /** * @param pcts 设定百分位数,可选值如99, 99.99. */ public Histogram(Double... pcts) { this(1, pcts); } /** * @param sampleRate 采样率,1代表100%, 2代表50%, 10 代表10% * @param pcts 百分位数,可选值如99, 99.99. */ public Histogram(Integer sampleRate, Double... pcts) { this.sampleRate = sampleRate; this.pcts = pcts; reset(); } public void update(long value) { if (sampleRate == 1) { //没有采样率 synchronized (this) { measurements.add(value); } } else if (sampleCounter.incrementAndGet() % sampleRate == 0) { //有采样率且命中采样 synchronized (this) { measurements.add(value); } } } /** * 计算单位时间内的metrics值, 存入该Metrics的Snapshot中,并清零原始数据. */ public HistogramMetric calculateMetric() { // 快照当前的数据,在计算时不阻塞新的metrics update. List<Long> snapshotList = null; synchronized (this) { snapshotList = measurements; measurements = new LinkedList<Long>(); } if (snapshotList.isEmpty()) { return createEmptyMetric(); } HistogramMetric metric = new HistogramMetric(); int count = snapshotList.size(); double sum = 0; if ((pcts != null) && (pcts.length > 0)) { // 按数值大小排序,以快速支持百分比 Collections.sort(snapshotList); metric.min = snapshotList.get(0); metric.max = snapshotList.get(count - 1); for (long value : snapshotList) { sum += value; } for (Double pct : pcts) { metric.pcts.put(pct, getPercent(snapshotList, count, pct)); } } else { // 不排序的算法,因为不需要支持百分位数 metric.min = snapshotList.get(0); metric.max = metric.min; for (long value : snapshotList) { if (value < metric.min) { metric.min = value; } if (value > metric.max) { metric.max = value; } sum += value; } } metric.avg = sum / count; latestMetric = metric; return metric; } private HistogramMetric createEmptyMetric() { HistogramMetric metric = new HistogramMetric(); for (Double pct : pcts) { metric.pcts.put(pct, 0L); } return metric; } /** * 在已排序的List中,取出百分位数上的值 */ private static Long getPercent(List<Long> snapshotList, int listCount, double pct) { double pos = (pct * (listCount + 1)) / 100; if (pos < 1) { return snapshotList.get(0); } if (pos >= listCount) { return snapshotList.get(listCount - 1); } return snapshotList.get((int) pos - 1); } public void reset() { synchronized (this) { this.measurements = new LinkedList<Long>(); } this.sampleCounter.set(0); this.latestMetric = createEmptyMetric(); } /** * 配置需要计算的百分位,如99, 99.99 */ public void setPcts(Double[] pcts) { this.pcts = pcts; } /** * 采样率,1代表100%,2代表50%,10 代表10% */ public void setSampleRate(int sampleRate) { if (sampleRate <= 0) { sampleRate = 1; } else { this.sampleRate = sampleRate; } } @Override public String toString() { return "Histogram [latestMetric=" + latestMetric + ", measurements=" + measurements + ", pcts=" + Arrays.toString(pcts) + "]"; } }