网站地图的形式,建设网站运营收入,百度注册公司网站,黑龙江省建设信息网官网承接上文
承接上文中的【深度挖掘Java性能调优】「底层技术原理体系」深入探索Java服务器性能监控Metrics框架的实现原理分析#xff08;Counter篇)#xff0c;我们知道和了解了对应的Counter计数器的作用和实现原理#xff0c;接下来我们需要进行分析和了解计量器#xf…承接上文
承接上文中的【深度挖掘Java性能调优】「底层技术原理体系」深入探索Java服务器性能监控Metrics框架的实现原理分析Counter篇)我们知道和了解了对应的Counter计数器的作用和实现原理接下来我们需要进行分析和了解计量器Gauge和直方图Histogram
计量器Gauge
计量器Gauge是度量和收集指标数据的重要工具之一是一种用于表示任意可变值的指标。它可以是数字、字符串或其他类型的数据。通过调用已注册的回调方法或读取已注册的变量计量器能够获取当前值。计量器适用于需要动态监测具体数值或状态的指标例如内存使用情况、CPU使用情况等。 使用计量器可以帮助开发人员监测和优化应用程序的性能和资源消耗。通过监测指标的变化可以及时做出调整和改进提高应用程序的效率和稳定性。 与Counter一样计量器也是一个数字类型的指标但和计数器不同的是它主要用于收集指标的瞬时值因此它是可变的。它的常用用法如下所示 使用Gauge进行记录以统计API的响应时间因为响应时间是可变的可能会有高低波动。统计CPU的负载可以了解系统的负荷情况。统计CPU的核心线程数和运行线程数以了解系统中正在运行的线程数量。统计操作系统的文件句柄数以监控文件资源的使用情况。
与Counter的对比分析
相对于Counter来说因为Gauge记录的只是一个瞬时值因此也不用考虑多线程下的竞争与冲突问题。下面是一个简单的案例代码
private final static class SimpleGauge {private volatile double value;private SimpleGauge(double value) {this.value value;}public Double getValue() {return this.value;}public void setValue(double value) {this.value value;}
}直方图Histogram
当我们不仅仅关注计数Counter或者是瞬时变量Gauge而是需要知道最大值最小值中位数平均值以及第99%的值时我们就需要用到直方图Histogram这个统计类型了。
主要作用
Histogram主要的用途是表示分布情况直方图用于测量和统计数据分布的情况。它会记录值的分布和频率并提供一些统计计算如最大值、最小值、平均值、标准差等。直方图适合用于衡量数据集的中心趋势和离散程度比如响应时间的分布。
数据统计难点分析
为了准确统计一个API的99%响应时间我们不能简单地记录所有响应时间并进行排序。由于API在不断被调用新的响应时间会不断产生因此这个方法无法得到准确的99%响应时间。
源码原理分析
为了解决这个问题可以使用Reservoir类来收集响应时间等数据。Reservoir实质上是一个数据池用于保存数据在进行统计时可以获取快照 (Snapshot) 来获取统计数据。
Reservoir类
Reservoir类是在codehale库中被使用的这个类基于蓄水池抽样算法它可以在固定的容量下持续保留最近的数据样本。每当有新的响应时间数据到来时Reservoir会根据一定的概率选择保留该数据样本以保持总体的分布情况。在需要进行统计时可以基于Reservoir的快照来获取相应的统计数据例如获取平均响应时间、99%响应时间等。
使用Reservoir类能够实现高效地统计数据而不需要记录和排序所有数据同时能够保持近似的分布情况为后续的数据分析提供准确可靠的结果。 了解了基本原理之后我们来看一下histogram的源码。
public class Histogram implements Metric, Sampling, Counting {private final Reservoir reservoir;private final LongAdder count;public Histogram(Reservoir reservoir) {this.reservoir reservoir;this.count new LongAdder();}//向histogram中增加新的数据实际上就是向数据池中添加数据public void update(int value) {update((long) value);}public void update(long value) {count.increment();reservoir.update(value);}Overridepublic long getCount() {return count.sum();}//获取Snapshot实际上也是通过数据池来获取Overridepublic Snapshot getSnapshot() {return reservoir.getSnapshot();}
}再来看看Snapshot的代码。
public class Snapshot {//最核心的方法用于获取第n%的值public double getValue(double quantile);private final long[] values;public double getMedian() {return getValue(0.5);}public double get75thPercentile() {return getValue(0.75);}/*省略部分getNthPercentile函数*/public long getMax();public double getMean();public long getMin();/*...*/
}从Snapshot中我们就基本能够得到我们想要的统计数据了。
来简单地了解一下数据池。定义了数据池以后我们就需要考虑更多的问题了比如说如何保证可以高性能地将数据写入数据池中以及如何保证数据池中数据量不会过大而占用过多的内存以及如何快速地取出快照。在Codahale metrics里面主要定义了三种数据池。
UniformReservoir 默认保存1028条记录每次进行update操作的时候首先会依次地将值填入1028条记录中当记录满了之后就会使用随机替换0 - 1027中的一条。因为是随机替换所以也不需要进行加锁和解锁。 - SlidingWindowReservoir **固定大小的数据池**从0到n-1填入数据不断循环。也不会进行加锁和解锁。
- SlidingTimeWindowReservoir **非固定大小的数据池**但是只会存储过去N秒的数据。使用ConcurrentSkipListMap进行存储。
- ExponentiallyDecayingReservoir **固定大小的数据池**。首先会逐个数据填满数据池随后会将老的数据替换为新的数据使用ConcurrentSkipListMap进行存储。可以说是SlidingWindowReservoir与SlidingTimeWindowReservoir的结合。当然还有其他的有效的方法是使用基于概率算法的数据结构例如特定数据结构如TDigest算法来实时估计99%的响应时间。这些方法基于近似统计的原理通过维护一个固定容量的滑动窗口或一个特定的数据结构来跟踪最近一段时间的响应时间分布。 总结概括
以上介绍的计数器、量规和直方图是监控数据中常用且基础的数据类型。它们提供了一些基本的功能和计算让我们能够更好地理解和监控应用程序的关键指标和数据。
在使用 Java 监控库时我们可以依据具体需求使用这些数据类型并利用其提供的方法和功能来收集、记录和分析监控数据。这些数据类型的使用有助于帮助我们了解应用程序的状态、性能和行为进而进行优化和改进。
服务器性能监控的要点和讨论
我们需要收集的是瞬时值、计数还是统计分布值在进行数据记录时如何保证高性能的写入/更新尽可能减少锁的使用同时如何确保数据的更新是合理的如何将指标数据汇总到一个地方以便于后续处理