直方图与百分位数
定时器和分布摘要支持收集数据以观察其百分位数分布。查看百分位数主要有两种方法:
-
百分位直方图:Micrometer 将值累积到底层直方图中,并将预定义的一组桶发送到监控系统。监控系统的查询语言负责根据此直方图计算百分位数。目前,只有 Prometheus、Atlas 和 Wavefront 通过
histogram_quantile
、:percentile
和hs()
分别支持基于直方图的百分位近似计算。如果你的目标是 Prometheus、Atlas 或 Wavefront,建议使用这种方法,因为你可以跨维度聚合直方图(通过对一组维度的桶值求和),并从直方图中得出可聚合的百分位数。 -
客户端百分位数:Micrometer 为每个仪表 ID(名称和标签的组合)计算百分位近似值,并将百分位值发送到监控系统。这种方法不如百分位直方图灵活,因为无法跨标签聚合百分位近似值。尽管如此,它为不支持基于直方图的服务器端百分位计算的监控系统提供了一定程度的百分位分布洞察。
以下示例构建了一个带有直方图的计时器:
Timer.builder("my.timer")
.publishPercentiles(0.5, 0.95) // median and 95th percentile // <1>
.publishPercentileHistogram() 2
.serviceLevelObjectives(Duration.ofMillis(100)) 3
.minimumExpectedValue(Duration.ofMillis(1)) 4
.maximumExpectedValue(Duration.ofSeconds(10))
publishPercentiles
:用于发布在应用程序中计算的百分位数。这些值在维度之间是不可聚合的。publishPercentileHistogram
:用于发布适合在 Prometheus(通过使用histogram_quantile
)、Atlas(通过使用:percentile
)和 Wavefront(通过使用hs()
)中计算可聚合(跨维度)百分位数近似值的直方图。对于 Prometheus 和 Atlas,生成的直方图中的桶是由 Micrometer 预设的,基于 Netflix 经验确定的生成器,该生成器在大多数实际计时器和分布摘要上产生合理的误差范围。默认情况下,生成器生成 276 个桶,但 Micrometer 仅包括由minimumExpectedValue
和maximumExpectedValue
设置的范围内的桶(包括边界)。默认情况下,Micrometer 将计时器限制在 1 毫秒到 1 分钟的范围内,每个计时器维度生成 73 个直方图桶。publishPercentileHistogram
对不支持可聚合百分位数近似的系统没有影响。这些系统不会生成直方图。serviceLevelObjectives
:用于发布由您的 SLO 定义的累积直方图。当与publishPercentileHistogram
结合使用在支持可聚合百分位数的监控系统上时,此设置会向发布的直方图中添加额外的桶。当在不支持可聚合百分位数的系统上使用时,此设置会导致仅发布包含这些桶的直方图。minimumExpectedValue
/maximumExpectedValue
:控制由publishPercentileHistogram
生成的桶的数量,并控制底层 HdrHistogram 结构的准确性和内存占用。
由于将百分位数发送到监控系统会生成额外的时间序列,因此通常不建议在作为应用程序依赖项的核心库中配置它们。相反,应用程序可以通过使用仪表过滤器为某些计时器和分布摘要启用此行为。
例如,假设我们在一个公共库中有几个计时器。我们为这些计时器的名称添加了 myservice
前缀:
registry.timer("myservice.http.requests").record(..);
registry.timer("myservice.db.requests").record(..);
我们可以通过使用仪表过滤器来为两个计时器开启客户端百分位数功能:
registry.config().meterFilter(
new MeterFilter() {
@Override
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
if(id.getName().startsWith("myservice")) {
return DistributionStatisticConfig.builder()
.percentiles(0.95)
.build()
.merge(config);
}
return config;
}
});