延迟指标与测量:响应时间、吞吐量、百分位延迟

到底该看哪个指标?

很多团队一说到延迟优化,就盯着平均响应时间不放。说实话,这指标最容易骗人。我接手过一个水印服务,平均延迟只有 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 停顿、网络抖动。
避坑指南: 我曾经用平均数做 SLA,结果用户投诉时拿平均值去对质,根本解释不通。后来改用了 P99 < 500ms 作为 SLA 红线,才算真正盯住了体验。记住——平均值是给老板看的,百分位才是给自己看的。

如何测量水印接口的延迟?

测量不是装个监控工具就完事。你得从三个层面下手:

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 已经打满,那瓶颈就在计算资源上——方向完全不同。

说了这么多指标,我画了张图,把这三者的关系串起来:

水印接口延迟测量的三个维度 延迟测量 响应时间 T1 + T2 + T3 + T4 吞吐量 QPS / TPS 百分位延迟 P50 / P90 / P99 三者关系 响应时间影响用户体验,吞吐量决定系统容量,百分位延迟暴露异常点。 优化时要同时跟踪三个指标,不能只盯一个。

测量工具的选型建议

业内常用的工具有几类,我简单列个对比:

工具 适用场景 优点 缺点
Prometheus + Grafana 服务端细粒度监控 支持高基数、灵活聚合 需要自己埋点
Jaeger / Zipkin 分布式调用链追踪 能看到整条链路各环节耗时 采样率低了不准,高了压力大
JMeter / Locust 压测 能模拟真实并发,出百分位报告 压测机本身可能成为瓶颈
StatsD + Datadog 快速集成的全栈监控 开箱即用,告警完善 成本高,数据粒度受控

我个人比较推荐小团队先用 Prometheus + Grafana,自己写几个埋点,把 P50/P90/P99 和 QPS 画到一张图里。再配合 Jaeger 做抽样追踪,基本能覆盖 90% 的排查场景。千万别一上来就上商业方案,贵不说,还得花时间调教——等你调好了,老板早催你出优化结果了。

总结一下测量要点

  • 不要只测一次, 要持续测量,覆盖不同时间段和负载。
  • 百分位比平均值更有价值, 特别是 P99。
  • 响应时间要分段拆解, 不然你永远不知道瓶颈在哪段。
  • 吞吐量和延迟必须一起看, 单优化一个往往会导致另一个变差。

曾经有个服务,我一上手就把 P99 从 800ms 优化到了 100ms。正高兴呢,运维说吞吐量掉了一半。后来发现我加了个全量缓存,缓存刷新时锁住了整个服务。这就是典型为了延迟牺牲吞吐量的反面案例。所以,三样指标必须一起盯,缺一不可。