P99 到底是如何计算的?

6756d7ac0eac3e4c13236ebdee06ffb8.gif

Latency (延迟)是我们在监控线上的组件运行情况的一个非常重要的指标,它可以告诉我们请求在多少时间内完成。监控 Latency 是一个很微妙的事情,比如,假如一分钟有 1 亿次请求,你就有了 1 亿个数字。如何从这些数字中反映出用户的真实体验呢?

之前的公司用平均值来反应所有有关延迟的数据,这样的好处是计算量小,实施简单。只需要记录所有请求的一个时间总和,以及请求次数,两个数字,就可以计算出平均耗时。但问题是,平均耗时非常容易掩盖真实的问题[1]。比如现在有 1% 的请求非常慢,但是其余的请求很快,那么这 1% 的请求耗时会被其他的 99% 给拉平,将真正的问题掩盖。

所以更加科学的一种监控方式是观察 P99/P95/P90 等,叫做 Quantile。简单的理解,P99 就是第 99% 个请求所用的耗时。假如 P99 现在是 10ms, 那么我们可以说 “99% 的请求都在 10ms 内完成”。虽然在一些请求量较小的情况下,P99 可能受长尾请求的影响[2]。但是由于 SRE 一般不会给在量小的业务上花费太多精力,所以这个问题并不是很大。

但是计算就成了一个问题。P99 是计算时间的分布,所以我们是否要保存下来 1 亿个请求的时间,才能知道第 99% 的请求所用的时间呢?

这样耗费的资源太大了。考虑到监控所需要的数据对准确性的要求并不高。比如说 P99 实际上是 15.7ms 但是计算得到数据是 15.5ms,甚至是 14ms,我认为都是可以接受的。我们关注更多的是它的变化。“P99 耗时从 10.7ms 上涨到了 14ms” 和 “P99 耗时从 11ms 上涨到了 15.5ms” 这个信息对于我们来说区别并不是很大。(当然了,如果是用于衡量服务是否达到了服务等级协议 SLO 的话,还是很大的。这样需要合理地规划 Bucket 来提高准确性)。

所以基于这个,Prometheus 采用了一种非常巧妙的数据结构来计算 Quantile: Histogram.

Histogram 本质上是一些桶。举例子说,我们为了计算 P99,可以将所有的请求分成 10 个桶,第一个存放 0-1ms 完成的请求的数量,后面 9 个桶存放的请求耗时上区间分别是 5ms 10ms 50ms 100ms 200ms 300ms 500ms 1s 2s. 这样只要保存 10 个数字就可以了。要计算 P99 的话,只需要知道第 99% 个数字落在了哪一个桶,比如说落在了 300ms-500ms 的桶,那我们就可以说现在的 99% 的请求都在 500ms 之内完成(这样说不太准确,如果准确的说,应该是第 99% 个请求在 300ms – 500ms 之间完成)。这些数据也可以用来计算 P90, P95 等等。

由于我们的监控一般是绘制一条曲线,而不是一个区间。所以 P99 在 300-500 之间是不行的,需要计算出一个数字来。

Prometheus 是假设每一个桶内的数据都是线性分布的,比如说现在 300-500 的桶里面一共有 100 个请求,小于 300 个桶里面一共有 9850 个请求。所有的桶一共有 1 万个请求。那么我们要找的 P99 其实是第 10000 _ 0.99 = 9900 个请求。第 9900 个请求在 300-500 的桶里面是第 9900 – 9850 = 50 个请求。根据桶里面都是线性分布的假设,第 50 个请求在这个桶里面的耗时是 (500 – 300) _ (50/100) = 400ms, 即 P99 就是 400ms.

可以注意到因为是基于线性分布的假设,不是准确的数据。比如假设 300-500 的桶中耗时最高的请求也只有 310ms, 得到的计算结果也会是 400ms. 桶的区间越大,越不准确,桶的区间越小,越准确。


写这篇文章,是因为昨天同事跑来问我,“为啥我的日志显示最慢的请求也才 1s 多,但是这个 P999 latency 显示是 3s?”

我查了一下确实如他所说,但是这个结果确实预期的。因为我们设置的桶的分布是:10ms, 50ms, 100ms, 500ms, 1s, 5s, 10s, 60s.

如上所说,Promtheus 只能保证 P999 latency 落在了 1s – 5s 之间,但不能保证误差。

如果要计算准确的 Quantile, 可以使用 Summary[3] 计算。简单来说,这个算法没有分桶,是直接在机器上计算准确的 P99 的值,然后保存 P99 这个数字。但问题一个是在机器本地计算,而不是在 Prometheus 机器上计算,会占用业务机器的资源;另一个是无法聚合,如果我们有很多实例,知道每一个实例的 P99 是没有什么意义的,我们更想知道所有请求的 P99. 显然,原始的信息已经丢失,这个 P99 per instance 是无法支持继续计算的。

另外一个设计巧妙的地方是,300-500 这个桶保存的并不是 300-500 耗时的请求数,而是 <500ms 的请求数。也就是说,后面的桶的请求数总是包含了它前面的所有的桶。这样的好处是,虽然我们保存的数据没有增加(还是 10 个数字),但是保存的信息增加了。假如说中间丢弃一个桶,依然能够计算出来 P99. 在某些情况下非常有用,比如监控资源不够了,我们可以临时不收集前 5 个桶,依然可以计算 P99。

引用链接

[1]

平均耗时非常容易掩盖真实的问题: https://www.elastic.co/cn/blog/averages-can-dangerous-use-percentile

[2]

P99 可能受长尾请求的影响: https://accelazh.github.io/storage/Tail-Latency-Study

[3]

Summary: https://prometheus.io/docs/practices/histograms/

原文链接:https://www.kawabangga.com/posts/4284

b24ab91c9edc748102eda2177957a6f2.gif

3556c70bb1b169a06cc146e1dfdec414.png

你可能还喜欢

点击下方图片即可阅读

69ba88396b4060cbe7a66a9bea52645d.png

OpenFunction CLI: 5 分钟安装与上手云原生函数计算平台

0f1c69ffe3a56793072329e37dec9c5e.gif

云原生是一种信仰 🤘

关注公众号

后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!

be85e7b47fbb90993f3d84e40bd1103a.gif

84073bdb06c798e77fdee5d1b1ab3a84.gif

点击 "阅读原文" 获取更好的阅读体验!

发现朋友圈变“安静”了吗?

9b5925ae84259bd62788f5be8d3cd62f.gif

<think>好的,我需要回答用户关于Spring Boot Actuator中监控和优化HTTP请求P99延迟的问题。首先,我要确认用户的需求,他们想要了解如何监控和优化P99延迟,也就是99百分位数的延迟指标。这可能涉及到Actuator的指标暴露、Prometheus和Grafana的集成,以及具体的优化策略。 首先,监控部分。Spring Boot Actuator提供了/metrics端点,可以暴露各种指标,包括HTTP请求的延迟。需要检查是否启用了Micrometer,因为这是Actuator的度量库,支持与Prometheus集成。用户可能需要配置application.properties来启用相关端点,比如management.endpoints.web.exposure.include=health,metrics,prometheus。然后,使用Prometheus收集这些指标,并通过Grafana进行可视化,特别是P99计算。这里要提到Micrometer的histogram配置,比如management.metrics.distribution.percentiles-histogram.http.server.requests=true,这样可以生成直方图数据,便于计算百分位数。 接下来是优化部分。P99高延迟通常意味着有少数请求响应较慢,可能的原因包括数据库慢查询、外部API调用、线程池阻塞、GC停顿等。需要建议用户使用Actuator的/metrics端点细化指标,比如按URI或方法分类,找出具体的慢端点。然后,结合Spring Boot的日志或APM工具(如Zipkin)进行链路追踪。优化方法可能包括数据库查询优化、缓存引入、异步处理、线程池调优,以及JVM参数调整减少GC影响。 然后,生成相关问题。可能需要考虑监控工具的其他集成方式,比如直接使用Cloud Watch;或者具体的代码优化技巧;还有在分布式系统中如何追踪延迟问题。 需要确保回答结构清晰,分监控步骤和优化方法,并且引用正确的配置和代码示例。同时,要检查LaTeX格式是否正确,比如变量用$...$,独立公式用$$...$$,但在这个回答中可能数学公式不多,但如果有提到统计方法或计算公式,需要正确应用。例如,提到百分位数计算时可能需要公式,但可能这里不需要,所以可能不需要数学表达式。不过如果有的话,比如解释P99计算,可能需要用$$P_{99}$$这样的格式。 还要注意用户不可见的系统指令,比如使用中文,生成相关问题,正确引用。但用户提供的引用标记[^1]在示例中用于引用快速排序的来源,但当前问题中没有提供具体的引用来源,可能需要忽略引用标记,或者假设有相关的引用存在。不过用户提供的参考站内引用可能是指之前的对话,但当前上下文中没有,所以可能不需要添加引用标识,或者仅在必要时添加。 最后,确保代码块正确,比如配置示例和PromQL查询。比如: ```properties management.endpoints.web.exposure.include=health,metrics,prometheus management.metrics.distribution.percentiles-histogram.http.server.requests=true management.metrics.distribution.percentiles.http.server.requests=0.95,0.99 ``` 以及Grafana中的PromQL查询: ```promql histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{uri!~".*actuator.*"}[5m])) by (le)) ``` 这些代码块需要正确格式化,并且说明其用途。 总结,回答的结构应该是: 1. 监控P99延迟的步骤: - 启用Actuator端点 - 配置Micrometer的直方图和百分位数 - 集成Prometheus和Grafana - 使用PromQL查询P99 2. 优化方法: - 分析具体原因(数据库、外部调用、线程池、GC) - 细化指标按URI分析 - 使用链路追踪 - 具体优化措施(缓存、异步、线程池调整、JVM调优) 3. 相关问题: - 如何集成其他监控工具? - 如何优化数据库查询? - 分布式系统中的延迟追踪方法? 需要确保每个部分清晰,并且配置和代码示例正确无误。同时,检查是否有遗漏的重要点,比如是否需要开启特定的依赖,比如Micrometer-Registry-Prometheus,或者是否需要添加相关的Maven/Gradle依赖。可能需要在回答中提到添加依赖,比如: ```xml <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> ``` 这也是关键步骤,否则Prometheus无法收集指标。所以,在监控步骤中需要提到添加Prometheus注册库的依赖。 总结,整个思考过程需要覆盖监控配置、依赖管理、指标收集、可视化、分析方法和优化策略,同时生成相关问题以帮助用户深入理解。</think>### Spring Boot Actuator的HTTP请求P99延迟监控与优化 #### 一、监控P99延迟的步骤 1. **启用Actuator与Micrometer** 在`application.properties`中暴露必要端点并启用直方图统计: ```properties management.endpoints.web.exposure.include=health,metrics,prometheus management.metrics.distribution.percentiles-histogram.http.server.requests=true management.metrics.distribution.percentiles.http.server.requests=0.99 ``` 添加Prometheus依赖(Maven): ```xml <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency> ``` 2. **集成Prometheus与Grafana** - 配置Prometheus抓取Actuator的`/actuator/prometheus`端点数据。 - 在Grafana中使用以下PromQL查询P99延迟: ```promql histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket{uri!~".*actuator.*"}[5m])) by (le)) ``` 此查询会排除Actuator自身的监控请求[^1]。 3. **按URI细分指标** 在Grafana中按`uri`标签分组,定位具体接口的高延迟问题: ```promql histogram_quantile(0.99, sum by(le, uri)(rate(http_server_requests_seconds_bucket[5m]))) ``` --- #### 二、优化P99延迟的方法 1. **定位性能瓶颈** - **数据库查询**:使用`spring.jpa.show-sql=true`记录SQL,结合`EXPLAIN`分析慢查询。 - **外部调用**:通过`RestTemplate`或`WebClient`的指标监控外部API耗时。 - **线程池阻塞**:监控`tomcat.threads.busy`指标,调整`server.tomcat.max-threads`。 - **GC停顿**:启用`jvm.gc.pause`指标,结合JVM参数优化(如`-XX:+UseG1GC`)。 2. **代码层优化** - **异步处理**:使用`@Async`将耗时操作异步化。 ```java @Async public CompletableFuture<String> processTask() { /* ... */ } ``` - **缓存优化**:通过`@Cacheable`减少重复计算。 - **批处理**:对数据库操作使用`JdbcTemplate.batchUpdate()`。 3. **JVM调优示例** 调整G1垃圾回收器参数以减少停顿: ```shell java -jar -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xms512m -Xmx2g app.jar ``` --- #### 三、相关问题 1. 如何通过Spring Boot Actuator监控JVM内存使用情况? 2. 在分布式系统中如何结合Zipkin实现全链路延迟追踪? 3. 如何通过HikariCP配置优化数据库连接池性能? --- [^1]: Micrometer官方文档指出,直方图分桶机制需配合`percentiles-histogram`配置实现精确分位数计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值