技术指南

网络性能诊断:基线、瓶颈与可验证回滚

用固定路径和成对样本拆分 DNS、连接、首字节、吞吐与丢包,再以单变量 canary 验证回滚。

网络性能回归不是先找一个优化参数,而是先固定用户路径、协议、负载和时间窗,再把 DNS、连接、首字节、传输、丢包、重传、CPU 与应用延迟放进同一条证据链。只有候选变更在相同样本上改善目标指标、没有伤害保护指标,并且能撤销,才值得保留。

来源列出大量工具,本文只保留可复查的回归方法

归档来源覆盖 Speedtest、iPerf3、MTR、NextTrace、磁盘测试、进程流量、BBR、sysctl、第三方内核、Smokeping 和自动通知。可保留的是固定条件、多方向测量、长期趋势、单变量修改与回退意识;线路品牌排名、固定地区延迟、80% 达标线、一次性跑分、通用大缓冲模板、全局禁用 IPv6、第三方内核安装和带密钥通知脚本均不进入本文。磁盘 I/O 与应用 profiling 也应独立验收,不能混进网络结论。

先写回归问题,而不是先选工具

问题主指标保护指标固定条件
用户打开页面变慢DNS、connect、TLS、TTFB、totalHTTP 状态、内容摘要、错误率同地区、URL、协议、缓存状态
单次下载变慢单流吞吐、RTT、重传CPU、磁盘、服务端限速同文件、方向、时长、时段
并发业务退化业务吞吐、p95/p99错误率、队列、CPU softirq同并发模型和数据集
某运营商间歇异常端点丢包、路径与业务请求其他运营商和反向样本同协议、方向、目标、时间窗

阈值来自业务 SLO、供应商合同或自己的历史分布,不来自美西必须低于多少毫秒之类通用表格。保存原始样本和环境指纹,再计算中位数、尾部、波动与置信范围;单个最好值和自动选择的测速节点不能代表用户路径。

把环境指纹和原始样本一起保存

TARGET_IP 必须是获准测试的端点;目录可能包含内部地址,按敏感运行证据保护
run_id=$(date -u +%Y%m%dT%H%M%SZ)
out=network-${run_id}
mkdir -m 0700 "${out}"

{
  date -u --iso-8601=seconds
  uname -a
  ip -brief address
  ip route show table all
  sysctl net.ipv4.tcp_congestion_control
  ss -s
} >"${out}/environment.txt"

ping -n -c 30 -i 0.2 TARGET_IP >"${out}/ping.txt"
mtr --report --wide --report-cycles 30 --no-dns TARGET_IP \
  >"${out}/mtr-icmp.txt"
  • 记录 UTC、客户端和服务端位置、地址族、目标 IP、协议、方向、时长、并发与工具版本。
  • 至少保留一个完整业务窗口,并在同一时段重复基线与候选,避免把昼夜波动归因于配置。
  • 把失败和超时留在分母中,不只保存成功请求。
  • 同一批次同时保存应用状态、响应摘要、主机 CPU/内存和接口计数。
  • 开始前写停止条件,测试负载触碰错误率、带宽或 CPU 预算就终止。

按 DNS、连接、服务与传输分层定位

可观察证据不能直接推出
DNS解析耗时、A/AAAA、权威与递归结果目标端口一定可达
TCP/TLSconnect、appconnect、握手错误应用处理一定健康
HTTPTTFB、状态、total、响应大小/摘要链路吞吐上限
路径MTR 各跳响应、终点丢包、路径变化不回复探测的中间路由器正在丢转发流量
TCP 会话RTT、rttvar、cwnd、retrans、pacing瓶颈必然属于网络而非发送端 CPU/应用

用 curl 拆开连接、首字节和总耗时

若目标没有某个地址族,记录失败而不是删掉样本;不要在命令行放令牌或 Cookie
for family in 4 6; do
  for sample in $(seq 1 10); do
    printf 'family=%s sample=%s ' "${family}" "${sample}"
    curl --ipv${family} --silent --show-error --fail \
      --connect-timeout 5 --max-time 20 --output /dev/null \
      --write-out 'code=%{response_code} remote=%{remote_ip} dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total} bytes=%{size_download} speed=%{speed_download}\n' \
      https://www.example.com/health
  done
done

curl 当前把 time_connect 定义为到 TCP 连接完成的累计时间,把 time_appconnect 定义为 TLS 等应用连接完成时间,把 time_starttransfer 定义为首字节累计时间;TTFB 同时包含此前阶段和服务端处理。speed_download 是整次下载的平均字节/秒。对同一 URL 重复采样,才能区分解析、握手、服务端和传输阶段。

MTR 的中间跳丢包只是一条线索

使用受控测试地址;TCP 探测也可能被策略过滤,必须与实际 HTTPS 请求相互印证
target=203.0.113.20

mtr --report --wide --report-cycles 100 --no-dns "${target}"
mtr --tcp --port 443 --report --wide --report-cycles 100 \
  --no-dns "${target}"
ping -n -c 100 -i 0.2 "${target}"

吞吐测试要分清单流、反向与并行

先在云防火墙和主机防火墙只允许测试端地址;结束后确认进程退出并撤销临时规则
# 服务端仅绑定测试网络,单次连接后退出
iperf3 --server --one-off --server-max-duration 30 \
  --bind TEST_SERVER_IP --port 5201

# 客户端固定服务端、端口、时长并保存 JSON
iperf3 --client TEST_SERVER_IP --port 5201 --time 15 --json \
  >single-forward.json
iperf3 --client TEST_SERVER_IP --port 5201 --time 15 --reverse --json \
  >single-reverse.json
iperf3 --client TEST_SERVER_IP --port 5201 --time 15 --parallel 4 --json \
  >parallel-four.json

iPerf3 当前文档说明默认 TCP 5201 控制连接,reverse 让服务端发送,parallel 创建多流,JSON 便于保留结果。多流高于单流可能暴露单流窗口、CPU 或拥塞控制差异,也可能只是更强地占满链路;它不是用户体验更好的自动证明。测试时同时记录发送端/接收端、CPU、重传与业务影响。

把 socket 与主机资源放进同一时间窗

接口名按实际环境替换;计数器通常是累计值,应取同一时间窗的差值
ss -s
ss -tin state established
nstat -az | sed -n '/TcpRetransSegs\|TcpExtTCPLoss\|IpInDiscards/p'
ip -s link show dev eth0
mpstat -P ALL 1 5

ss -i 可显示 RTT、RTT 波动、拥塞算法、cwnd、重传、pacing 与已确认字节等 TCP 内部信息。若吞吐上升同时 CPU 饱和、重传增加或业务尾延迟恶化,就不能只凭 Mbps 接受候选。先排除应用限速、磁盘、虚拟化 steal、softirq 和对端瓶颈,再讨论内核参数。

常见现象要保留多个可证伪解释

现象优先核对常见误判
ping 稳定但页面慢DNS/TLS/TTFB、应用日志、缓存把服务端处理归为线路
并行快、单流慢RTT、cwnd、重传、CPU、对端限速直接换拥塞算法
晚间才退化同路径多日趋势、运营商与业务指标用一次白天 Speedtest 否认
某中间跳高丢包终点/后续跳、TCP 443 与真实请求把 ICMP 限速当故障点
IPv6 偶发慢A/AAAA、v4/v6 connect/TLS/route全局禁用 IPv6

变更前先证明它对应观察到的瓶颈

  1. 候选只改变一个控制项,并写明机制、预期改善、保护指标、停止条件和回滚命令。
  2. 读取当前有效值和可用选项;不要把通用 sysctl 大数值追加到主配置。
  3. 先在 canary 或隔离环境验证语法、启动、连通和恢复,再进入真实路径。
  4. 复用完全相同的目标、时段、方向、负载和样本数比较基线/候选。
  5. 未达到预先写定的业务阈值,或保护指标变差,就撤销而不是继续叠加参数。

IPv4 与 IPv6 分开测,不修改全局优先级猜答案

示例地址属于文档保留网段;替换为自己的实际目标
getent ahosts www.example.com
ip -4 route get 198.51.100.20
ip -6 route get 2001:db8::20
curl -4 --silent --show-error --output /dev/null \
  --write-out 'v4 remote=%{remote_ip} connect=%{time_connect} total=%{time_total}\n' \
  https://www.example.com/
curl -6 --silent --show-error --output /dev/null \
  --write-out 'v6 remote=%{remote_ip} connect=%{time_connect} total=%{time_total}\n' \
  https://www.example.com/

若某地址族稳定失败,继续核对 DNS、默认路由、MTU、云网络和应用 Happy Eyeballs 行为。临时软件级强制地址族可以帮助定位,但不是永久修复;全局改 gai.conf 或禁用 IPv6 会改变所有服务的故障模式,必须单独评审。

用成对样本验收,而不是比较两个最好值

  • 在同一窗口交替采集 baseline 与 candidate,避免时间趋势偏向其中一组。
  • 分别报告成功率、超时、p50/p95/p99、吞吐分布和重传差值,不删除异常值。
  • 真实业务 canary 与主动探测同时存在:主动测试解释链路,业务指标决定是否扩大。
  • 至少覆盖两个方向和实际地址族;必要时覆盖单流与代表性并发。
  • 扩大范围后重新采样,不把 canary 的 CPU、路径或邻居条件外推到全部实例。

回滚要恢复有效值,并证明性能与业务一起恢复

触发条件可以是错误率、超时、尾延迟、重传、CPU softirq、连接队列或吞吐保护线恶化。停止新测试流量,撤销候选 drop-in、qdisc、路由或应用配置,重载对应组件;再以同一探针证明有效值、连接、响应摘要和性能回到基线范围。不要用重启掩盖不知道哪个参数生效,也不要保留无归属的测试端口。

隔离演练证明了指标响应与完整回滚

VPScope 在一次性 Linux network namespace 中启用 loopback,并在 127.0.0.1:18327 启动 Python HTTP 源站。基线 20 次 ping 的平均 RTT 为 0.053 ms;仅在该 namespace 的 loopback 加入 30 ms netem 单向延迟后,平均往返 RTT 为 60.806 ms,curl 平均 connect 从 0.000215 秒升至 0.060558 秒,TTFB 从 0.001924 秒升至 0.122443 秒。

随后独立施加 25% 随机丢包,40 个往返样本观察到 32.5% 往返丢失;这不是校准后的单向损失率,只证明样本和指标会响应受控故障。删除 qdisc 后 RTT 回到 0.045 ms,connect 回到 0.000191 秒。宿主 loopback qdisc 前后完全一致,宿主不存在 18327 监听。该演练不证明公网路径、运营商、iPerf3 吞吐、BBR、NIC、云 QoS 或真实业务收益。

网络性能变更前的可复查清单

  • 问题、用户路径、业务 SLO、主指标、保护指标和停止条件已经写明。
  • 环境指纹、地址族、目标、协议、方向、时段、时长、并发和工具版本均随原始样本保存。
  • DNS、connect、TLS、TTFB、total 与吞吐分开,失败和超时保留在分母中。
  • MTR 中间跳只作为线索,并以终点、业务协议和实际请求相互印证。
  • 单流、反向和并行吞吐各自有目的,没有把最高 Mbps 当成用户体验。
  • socket RTT/cwnd/retrans、接口计数、CPU、应用错误率和尾延迟在同一窗口采集。
  • 阈值来自业务或历史分布,没有使用固定地区延迟、线路品牌或 80% 通用标准。
  • 候选只改一个控制项,已在 canary 中用成对样本比较,并监控保护指标。
  • 回滚命令、有效值、配置来源和测试端口清理已经验证。
  • 隔离测试的边界清楚,公网、供应商和真实用户结论仍需在实际路径复测。

返回知识库