AIGC标识 用 Python 测一组 IP 的延迟抖动:P50/P95/P99 怎么算

用 Python 测一组 IP 的延迟抖动:P50/P95/P99 怎么算

之前排查一个接口偶尔卡的问题,日志里平均响应时间才 80ms,看着挺正常。但用户就是反馈"偶尔卡一下"。我单独拉了一小组延迟数据,发现 P95 直接到 500ms 多。平均值这东西,最会骗人。

平均值到底骗在哪

假设你测了 10 次延迟:

  • 9 次是 20ms
  • 1 次是 500ms

平均下来 68ms,看起来一切安好。但用户体验到的是:大部分时候挺快,但偶尔一次明显卡顿。P95 就是 500ms,直接告诉你——有 5% 的请求会这么慢。

做压测或者挑网络出口,P50、P95、P99 一起看好歹心里有底。尤其是尾部延迟,往往藏着网络抖动、路由绕行、对端偶发瓶颈这些坑。

我为什么用 TCP 连接代替 ping

ping 走的是 ICMP,很多网络设备对 ICMP 和 TCP 的处理优先级不一样。再加上真实业务请求本身就是 TCP 握手之后才开始传数据,所以我自己测延迟时,更倾向于直接看 socket.connect() 用了多久。

connect() 开始到返回,SYN、SYN-ACK、ACK 已经走完了。这个时间才是业务层真正关心的"建立连接用了多久"。

口径统一很重要,不然拿 ping 测出来的延迟和 TCP 握手延迟比,完全没有意义。

脚本不长,但够用了

import socket
import time
import statistics

def measure_latency(host, port=443, timeout=5, n=30):
    results = []
    for _ in range(n):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(timeout)
        t0 = time.time()
        try:
            s.connect((host, port))
            results.append((time.time() - t0) * 1000)
        except Exception:
            results.append(None)
        finally:
            s.close()
        time.sleep(0.1)
    return results

def percentile(data, q):
    data = sorted(data)
    idx = q * (len(data) - 1)
    low = int(idx)
    high = min(low + 1, len(data) - 1)
    w = idx - low
    return data[low] * (1 - w) + data[high] * w

hosts = [
    ("223.5.5.5", 53),
    ("8.8.8.8", 53),
    ("1.1.1.1", 53),
]

for host, port in hosts:
    raw = measure_latency(host, port, n=30)
    ok = [x for x in raw if x is not None]
    fail = len(raw) - len(ok)

    if not ok:
        print(f"{host}:{port} 全挂")
        continue

    print(f"{host}:{port}")
    print(f"  成功 {len(ok)}/{len(raw)}, 失败 {fail}")
    print(f"  avg={statistics.mean(ok):.1f}ms, P50={percentile(ok, 0.50):.1f}ms")
    print(f"  P95={percentile(ok, 0.95):.1f}ms, P99={percentile(ok, 0.99):.1f}ms")

这里拿 DNS 服务器的 53 端口练手,因为这几个目标基本 7×24 都在线,端口也开放。你要测自己的业务接口,直接改 hosts 里的 IP 和端口就行。

跑出来的结果大概长这样

223.5.5.5:53
  成功 30/30, 失败 0
  avg=15.7ms, P50=20.2ms
  P95=25.2ms, P99=25.5ms

8.8.8.8:53
  成功 30/30, 失败 0
  avg=15.3ms, P50=20.0ms
  P95=24.9ms, P99=25.3ms

注意 P50 和 P95 差得不多,说明这组数据抖动很小。如果你看到 P50 是 20ms,P95 直接飙到 200ms,那网络肯定存在偶发拥堵或路由不稳。

几个指标我是怎么看的

  • avg:顺手看看,但我不怎么信。异常值一拉,平均值就失真。
  • P50:中位数,一半请求比它快,一半比它慢。比平均值更稳。
  • P95:95% 的请求不会超过这个值。这个数高了,说明有一定比例的体验会明显变差。
  • P99:最尾部那 1%。如果业务对延迟非常敏感,比如实时交易、直播推流,这个指标不能忽略。
  • 失败率:超时或连接失败的比例。再低的延迟,只要失败率高,就是不可用。

没有 numpy 怎么办

statistics 模块里没有 percentile,所以我写了一个线性插值版本。思路和 numpy 的默认 linear 插值一致,日常够用。如果你环境里有 numpy,直接 np.percentile 更省事。

我一般在什么场景跑这个

  • 新切网络出口或者换 CDN 节点时,先把几个候选 IP 拉出来跑一圈,看分布。
  • 用户反馈"偶尔卡"但日志平均值正常时,重点看 P95/P99 和失败率。
  • 选 DNS 服务器或者网关时,测几个候选的抖动情况。
  • 做压测前,先确认基础网络延迟是否稳定,别一上来就压业务接口。

次数我一般跑 30 次左右。太少了分位数不准,太多了容易被目标限流或者把本地网络环境打毛。

参考

posted @ 2026-08-10 16:55  IP观察室  阅读(1)  评论(0)    收藏  举报