延迟指标与测量:响应时间、吞吐量、百分位延迟
到底该看哪个指标?
很多团队一说到延迟优化,就盯着平均响应时间不放。说实话,这指标最容易骗人。我接手过一个水印服务,平均延迟只有 30ms,看着挺美。可一到高峰期,用户就疯狂报“水印打不出来”。一查监控——P99 延迟飙到了 800ms。平均值好看,全是靠大量低延迟请求拉回来的,那 1% 的慢请求把用户体验全毁了。
所以,延迟测量不能只看一个数。你得同时关注三个维度:
- 响应时间 —— 一个请求从发出到收到完整数据的时间。
- 吞吐量 —— 单位时间内能处理多少请求。
- 百分位延迟 —— 最慢的一批请求到底有多慢。
响应时间:最基础的度量
响应时间通常分成两端:客户端视角和服务器视角。我习惯把服务器端的延迟拆成三段:网络传输、队列等待、实际处理。
| 阶段 | 说明 | 常见瓶颈 |
|---|---|---|
| T1(网络入) | 请求从客户端到达服务器网卡 | 带宽、DNS、TCP握手 |
| T2(队列等待) | 请求在服务线程池/事件循环中排队 | 线程数、连接池、IO模型 |
| T3(实际处理) | 业务逻辑、水印生成、图片处理 | CPU、内存、磁盘IO |
| T4(网络出) | 响应从服务器到达客户端 | 带宽、序列化大小 |
嗯,这里要注意:如果客户端的响应时间比服务器端记录的多出几十毫秒,多半是网络往返(RTT)在作祟。我曾经排查过一个水印接口,服务器端只花 5ms,客户端却等了 200ms。最后发现是 CDN 回源配置错了,每次请求都要跨机房路由。
吞吐量:别跟响应时间搞混
吞吐量(QPS/TPS)和响应时间是一对孪生兄弟,但很多人把它们对立起来。实际上,它们的关系像跷跷板。压力小时,响应时间很低;一旦压力超过某个临界点,响应时间会急剧上涨,吞吐量反而下降。这个临界点就是系统的最大吞吐能力。
经验法则: 优化时不要只压一个指标。先定目标——比如 P99 < 200ms,然后在保证 P99 不超标的前提下,把吞吐量推到最大。
我见过一个团队把水印接口的响应时间从 100ms 优化到 20ms,兴高采烈地上了线。结果负载一上来,数据库连接池先炸了——吞吐量反而比优化前还低。为什么?因为他们把异步处理改成了同步,虽然单次快了,但并发能力下降了。
百分位延迟:藏在不平均里的真相
百分位延迟分很多种:P50(中位数)、P90、P99、P99.9。我通常在监控面板上至少放四个:
- P50:代表大部分用户的体验,这个值高了说明系统普遍慢。
- P90:提示是否有少数请求卡住。
- P99:核心 SLA 指标,代表了最糟糕的 1% 用户。
- P99.9:通常由偶发故障引起,比如 GC 停顿、网络抖动。
如何测量水印接口的延迟?
测量不是装个监控工具就完事。你得从三个层面下手:
1. 埋点
在代码里埋入时间戳。我习惯在以下几处打点:
long start = System.nanoTime(); // 请求到达
// … 解析参数 …
long afterParse = System.nanoTime(); // 参数解析完
// … 获取水印模板、用户信息 …
long afterFetch = System.nanoTime(); // 数据准备完
// … 合成水印图片 …
long afterRender = System.nanoTime(); // 图片合成完
// … 返回响应 …
long end = System.nanoTime(); // 响应发出
然后把这些差值上报到时序数据库(比如 Prometheus、InfluxDB)。为什么要用纳秒?微服务的调用链一多,毫秒级精度根本不够用。我遇到过 case,水印合成只花了 3ms,但两次网络 IO 之间差了 50ms,如果用毫秒级日志,根本看不出这 50ms 花在哪。
2. 外部监控
内部埋点只反映服务端行为。用户侧的实际体验,还得靠外部拨测。我推荐在以下位置部署探针:
- 同城同机房:测最理想情况。
- 跨区域:测 CDN 和骨干网的效果。
- 弱网环境:测限速、丢包场景下的表现。
拨测结果和内部埋点对比一下,就知道网络层丢了多少延迟。
3. 持续压测
静态指标只能反映某个时刻的好。我坚持每轮发布前跑一次全链路压测,记录不同并发下的 P50、P99、吞吐量。这样就能画出一条“响应时间 vs 并发”的曲线。
一个小习惯: 每次压测时,我还会记录 CPU 和内存的变化。如果 P99 飙高但 CPU 没用满,那大概率是锁竞争或 IO 等待。如果 CPU 已经打满,那瓶颈就在计算资源上——方向完全不同。
说了这么多指标,我画了张图,把这三者的关系串起来:
测量工具的选型建议
业内常用的工具有几类,我简单列个对比:
| 工具 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Prometheus + Grafana | 服务端细粒度监控 | 支持高基数、灵活聚合 | 需要自己埋点 |
| Jaeger / Zipkin | 分布式调用链追踪 | 能看到整条链路各环节耗时 | 采样率低了不准,高了压力大 |
| JMeter / Locust | 压测 | 能模拟真实并发,出百分位报告 | 压测机本身可能成为瓶颈 |
| StatsD + Datadog | 快速集成的全栈监控 | 开箱即用,告警完善 | 成本高,数据粒度受控 |
我个人比较推荐小团队先用 Prometheus + Grafana,自己写几个埋点,把 P50/P90/P99 和 QPS 画到一张图里。再配合 Jaeger 做抽样追踪,基本能覆盖 90% 的排查场景。千万别一上来就上商业方案,贵不说,还得花时间调教——等你调好了,老板早催你出优化结果了。
总结一下测量要点
- 不要只测一次, 要持续测量,覆盖不同时间段和负载。
- 百分位比平均值更有价值, 特别是 P99。
- 响应时间要分段拆解, 不然你永远不知道瓶颈在哪段。
- 吞吐量和延迟必须一起看, 单优化一个往往会导致另一个变差。
曾经有个服务,我一上手就把 P99 从 800ms 优化到了 100ms。正高兴呢,运维说吞吐量掉了一半。后来发现我加了个全量缓存,缓存刷新时锁住了整个服务。这就是典型为了延迟牺牲吞吐量的反面案例。所以,三样指标必须一起盯,缺一不可。