ISSUE NO. 01 · 协议与终端手记

把黑盒拆成可复现的数字。

这里记录折腾网络、浏览器与 Shell 时整理的操作手记。每一条结论都附带本机可直接运行的命令与算式。

FIG 1.1 · CURL TIMING WATERFALL MODEL

单次 HTTPS 请求阶段差值推演标尺

理论总耗时 (time_total) 74.0 ms
DNS 解析 namelookup (示例 4ms)
4.0 ms
TCP 握手 (1×RTT) connect - namelookup
18.0 ms
TLS 1.3 协商 (1×RTT+4ms) appconnect - connect
22.0 ms
首包等待 (1×RTT+处理) starttransfer - appconnect
30.0 ms
响应体传输 total - starttransfer
0.0 ms

演算公式(TLS 1.3 新建连接):总耗时 = DNS(4ms) + TCP(1×RTT=18ms) + TLS(1×RTT+4ms=22ms) + TTFB(1×RTT+应用12ms=30ms) = 74.0 ms。可修改上方数字实时重算。

三篇技术手记

不写空泛概念,只留本机可验证的抓包与终端操作步骤。支持左侧切换单篇精读,或展开全部手记连续翻阅。

一次 HTTPS 请求慢在哪?用 curl 五个时间戳拆开延迟黑盒

排障时只看浏览器 Network 面板的总耗时,往往分不清是跨洋物理光缆慢、TLS 握手多跑了往返,还是后端应用阻塞在数据库查询。curl 内置的 -w 格式化变量能在不装任何抓包工具的前提下,把耗时精确切到毫秒。

一、单行命令打印五个累计时间点

在终端执行下面这条命令(可将末尾 URL 换成任意你想测试的 HTTPS 域名):

sh · 终端实测命令
curl -o /dev/null -s -w "dns: %{time_namelookup}s\ntcp: %{time_connect}s\ntls: %{time_appconnect}s\nttfb: %{time_starttransfer}s\ntotal: %{time_total}s\n" https://example.com

注意一个最容易踩的坑:curl 输出的五个变量全部是「从请求发起那一刻(0.000s)算起的累计时间」,而不是每个阶段单独的耗时。要想算出每一棒真正花了多少毫秒,需要做一次相邻相减:

阶段名称 差值计算公式 网络层真实动作与排障判据
DNS 解析 time_namelookup 本地解析器查询域名耗时。若超过 50ms,优先检查本机 /etc/resolv.conf 或上游递归 DNS 是否绕远。
TCP 握手 connect - namelookup 客户端发出 SYN 到收到 SYN+ACK 的耗时,直接反映客户端到节点机房的物理网络 1-RTT 基准延迟。
TLS 握手 appconnect - connect TLS 密钥协商与证书验证。开启 TLS 1.3 时约为 1 × RTT;若退回 TLS 1.2 且无会话复用,会膨胀到 2 × RTT。
首包等待 (TTFB) starttransfer - appconnect 等于 1 × RTT 传输加上服务端处理时长(网关反代 + 业务逻辑)。若 TCP 很短而此项偏长,可重点排查服务端处理或上游回源耗时。
正文下载 total - starttransfer 接收响应体字节耗时。大文件此项慢时,通常受限于 TCP 接收窗口或拥塞控制算法(如 Cubic 在丢包链路上的降速)。

二、连接复用对比:用一个 curl 命令连发两次请求

HTTP/1.1 默认开启 Keep-Alive,HTTP/2 则在单条 TCP+TLS 连接上多路复用。把同一个 URL 在一条 curl 命令里写两遍,就能亲眼看到连接复用省掉了多少时间:

sh · 验证 Keep-Alive / HTTP/2 连接复用
curl -o /dev/null -s -w "Req#1 -> tcp:%{time_connect}s tls:%{time_appconnect}s total:%{time_total}s\n" https://example.com \
     -o /dev/null -s -w "Req#2 -> tcp:%{time_connect}s tls:%{time_appconnect}s total:%{time_total}s\n" https://example.com

在跨洋链路上(例如往返 RTT 为 150ms 的场景),第一次新建连接时 TCP 与 TLS 1.3 握手合计需要约 2 × RTT(约 300ms);而紧接着的第二次请求因为直接复用了已打开的套接字,time_connect 和 time_appconnect 会直接显示为 0.000000s,省去了前两轮握手往返。这也是为什么在反代与上游之间配置长连接池(keepalive)能直接省掉重复建连开销。

INTERACTIVE WORKBENCH · 零依赖纯本地计算

字节基数、传输耗时与 BDP 窗口换算台

存储厂商与操作系统用不同的基数标称容量(1000 vs 1024),而高延迟链路上的吞吐上限往往卡在 TCP 接收窗口而非带宽。输入任意实测值或点选预设,两侧算式即时联动。

1. 文件传输耗时 & 带宽时延积 (BDP)

精确字节数 / 比特数 52,428,800 B 419,430,400 bits (50.00 MB 十进制)
实际传输耗时 (含 1-RTT 握手 + 帧头损耗) 4.61 s 理想物理线速耗时:4.19 s · 有效吞吐:11.19 MiB/s
带宽时延积 BDP (跑满带宽所需最小飞行窗口) 1,875,000 B (1.79 MiB) 若系统默认接收窗口为 64 KiB,单连接吞吐上限仅 3.50 Mbps
Linux TCP 缓冲区调优建议值 (基于当前 BDP 向上取整至 2 的幂)
sysctl -w net.ipv4.tcp_rmem="4096 131072 4194304" net.ipv4.tcp_wmem="4096 65536 4194304"

2. 十进制 (SI) 与二进制 (IEC) 基数对照表

实时随左侧载荷更新

网卡速率(Mbps/Gbps)和硬盘标称容量永远按 1000 进位;而操作系统内存页与 ls -lh 输出按 1024 进位。级别越高,两者的视觉落差越大:

量级 SI 十进制 (10³) IEC 二进制 (2¹⁰) 同数值容量差
千 (K / Ki) 52,428.80 KB 51,200.00 KiB 1 KiB = 1.024 KB (+2.4%)
兆 (M / Mi) 52.43 MB 50.00 MiB 1 MiB = 1.049 MB (+4.9%)
吉 (G / Gi) 0.0524 GB 0.0488 GiB 1 GiB = 1.074 GB (+7.4%)
太 (T / Ti) 0.000052 TB 0.000048 TiB 1 TiB = 1.100 TB (+10.0%)

为什么 100 Mbps 跑不出 12.5 MB/s?

标准以太网 MTU 为 1500 Bytes。扣除 IPv4 头(20B)、TCP 头(20B)与常见 TCP 时间戳选项(12B),单个包真正承载的应用层数据(MSS)为 1448 Bytes,再加上以太网帧头帧尾(38B):

有效载荷率 = 1448 / (1500 + 38) = 94.15%

因此 100 Mbps 物理带宽折算到应用层最大下载速度约为 11.77 MB/s(即 11.22 MiB/s),而非直觉除 8 算出的 12.5 MB/s。

单行排障速查表

不装额外第三方工具,仅靠系统自带标准组件即可定位常见网络与文件问题的 6 条实测命令。

TLS 证书检查

查看远端 443 端口当前生效证书的签发者、SAN 主体与精确到期时间(开启 SNI):

echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | openssl x509 -noout -dates -subject -issuer
端口与套接字

列出当前正在监听的 TCP/UDP 端口及对应的进程 PID(替代老旧的 netstat):

ss -tulpn | grep -E 'LISTEN|:443|:80'
DNS 解析比对

绕过本机缓存,直接向指定公共递归服务器查询 A 与 AAAA 记录及剩余 TTL:

dig +noall +answer example.com A example.com AAAA @1.1.1.1
磁盘占用定位

统计当前目录下一级子目录体积,按人类可读单位(K/M/G)从大到小排前十且不跨挂载点:

du -xhd1 /var/log 2>/dev/null | sort -hr | head -n 10
重定向链路追踪

自动跟随 301/302 跳转,直接输出最终落地的 HTTP 状态码、最终 URL 与经过的跳转次数:

curl -sIL -o /dev/null -w '%{http_code} -> %{url_effective} (redirects: %{num_redirects})\n' http://example.com
文件完整性校验

批量计算多个静态文件的 SHA-256 摘要,对比部署前后字节是否完全一致:

sha256sum index.html styles.css app.js