9性能之巅:系统、企业与云可观测性(第2版)

网络

协议栈、TCP 与网络性能

TCP拥塞控制重传网络栈XDP

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第9章 网络

协议栈、TCP 与网络性能

章节导读

网络是分布式系统的"物理定律":光速限制了下限(北京到弗吉尼亚单程光纤传播约 70ms,任何软件都无法突破),丢包与乱序是常态而非异常,而 Linux 网络栈的软件路径决定了主机侧能逼近硬件极限到什么程度。网络性能问题也是最难归因的——"网络慢"可能是应用(缓冲策略)、主机(栈配置、CPU 饱和)、路径(运营商、云网络)、对端(backlog 满、处理慢)中任何一环。本章沿数据包的路径拆解网络性能:从 socket API 到 TCP/IP 协议栈,到驱动与网卡,再到云网络与高性能旁路技术。

先看发送路径:应用 write()/send() → socket 层(sk_buff 分配、数据从用户缓冲拷入内核 socket 发送缓冲)→ TCP 层(分段为 MSS 大小、加序号、进发送队列、拥塞控制决定何时发)→ IP 层(路由查找、分片判断、netfilter/iptables 钩子)→ Qdisc(排队规则:默认 fq_codel/fq,整形与公平队列)→ 驱动(DMA 映射、描述符入网卡发送环)→ 网卡发出。接收路径:网卡 DMA 写入环形缓冲 → 硬中断 → NAPI 软中断轮询(NET_RX,批量收包减少中断)→ 协议栈处理(IP 校验、netfilter、TCP 状态机、乱序重排)→ socket 接收缓冲 → 应用 epoll 就绪 → read() 拷出。每个环节都有队列与缓冲,每个队列都可能溢出(丢包)或积压(延迟)。性能分析的本质是定位"包在哪个队列里花了多少时间"。

TCP 是网络性能的核心协议,其机制必须逐个理解。三次握手:1 个 RTT 建连(TCP Fast Open 可省数据往返)——短连接服务的握手税在跨地域时惊人(100ms RTT × 每请求一次握手 = 不可接受,连接复用是铁律)。慢启动:cwnd(拥塞窗口)从 10 个 MSS(约 14KB,Linux 默认 IW10)开始每 RTT 翻倍——小响应(<14KB)一个 RTT 发完,大响应需要多个 RTT 爬坡,这就是"大文件跨洋传输慢"的机制(带宽时延积 BDP = 带宽 × RTT,1Gbps × 100ms = 12.5MB,cwnd 要爬到 12.5MB 才能跑满——需要约 10 个 RTT)。拥塞控制算法:cubic(Linux 默认,窗口按三次函数增长,高带宽长肥管道表现好)、bbr(Google,基于带宽与 RTT 测量而非丢包信号,浅缓冲与有丢包路径上优势显著,但多流公平性有争议)、vegas/dctcp(数据中心低延迟场景)。丢包处理:快速重传(3 个重复 ACK 触发)、RTO 超时重传(指数退避,200ms 起——一次 RTO 就是巨大延迟毛刺)、TLP(尾丢包探测)。Nagle 算法(小包合并,TCP_NODELAY 关闭——交互式协议必关)与延迟 ACK(40-100ms 定时确认)的组合会造成经典的"40ms 卡顿"(Nagle 等 ACK、延迟 ACK 等数据,互相死等)。接收侧:rwnd(接收窗口)与自动调优(tcp_rmem 三元组:最小/默认/最大),应用读取慢会让窗口收缩反压发送方(应用层瓶颈伪装成网络瓶颈的经典案例)。

缓冲区与队列是延迟的温床。Bufferbloat(缓冲膨胀):过大的缓冲区在饱和时把"丢包信号"变成"排队延迟"(家用路由器 1-2 秒的缓冲让游戏延迟爆炸),解法是 AQM(主动队列管理):codel(控制延迟,超过目标 5ms 开始丢包/标记)、fq_codel(公平队列 + codel,Linux 默认 qdisc)、fq(pacing 匀速发送 + codel,高带宽服务推荐)、PIE。观测队列:tc -s qdisc show(dropped 计数)、/proc/net/softnet_stat(每 CPU 收包处理统计:第 2 列 dropped 是 backlog 满、第 3 列 time_squeeze 是软中断预算耗尽——网络 CPU 瓶颈的直接证据)。socket 级缓冲:ss -tm 的 skmem 字段(rb 接收缓冲占用/tb 发送缓冲占用,占用持续满 = 对端收不动或本端发太快)、netstat -s 的溢出计数(listen queue overflow、TCPBacklogDrop、PruneCalled 等)。

连接管理的高并发问题。TIME_WAIT:主动关闭方保持 2MSL(60 秒),高并发短连接服务(代理、爬虫)会积累数万 TIME_WAIT——它占用四元组与少量内存,但真正的杀手是本地端口耗尽(ip_local_port_range 约 28K 端口 × 60 秒 = 每秒 470 新连接的极限),解法是连接复用(keepalive)而非调 tw_reuse(有语义风险)。accept 队列(backlog):三次握手完成但应用未 accept 的连接排队,满了之后新完成的握手被丢弃(客户端表现为连接超时),观测 netstat -s 的 "listen queue overflowed" 与 ss -lnt 的 Recv-Q(当前积压)/Send-Q(backlog 上限)。半连接队列(SYN queue):SYN 收到但未完成握手的连接,SYN flood 或 backlog 小时溢出(tcp_max_syn_backlog、syncookies 兜底)。fd 限制:每连接一个 fd,ulimit -n 与 somaxconn 都要够。C10K 到 C10M 的演进:epoll(事件驱动,万级连接)→ SO_REUSEPORT(多进程各自 listen 同端口,内核四元组哈希分发,消除单 accept 队列瓶颈——nginx/HAProxy 标配)→ XDP/DPDK(内核旁路,百万级)。

高性能网络技术谱系。内核旁路:DPDK(用户态驱动 + 轮询模式,独占核,零拷贝零中断,延迟 μs 级、吞吐 10M+ pps,代价是核被轮询烧掉且失去内核栈功能——NFV、负载均衡器专用);XDP(eBPF 挂网卡驱动层,包进内核栈前处理:DROP/PASS/TX/REDIRECT,单核数百万 pps,DDoS 过滤与 L4 负载均衡(Facebook 的 Katran)的主流方案,保留内核生态)。内核优化:busy polling(SO_BUSY_POLL,应用轮询 socket 减少中断-唤醒延迟)、GRO/GSO(收/发方向合并小包为大包,摊薄每包协议处理成本)、RSS/RPS/RFS(多队列收包分散到多核:RSS 硬件哈希、RPS 软件版、RFS 让包流向处理该连接的核——缓存亲和)、中断合并(ethtool -c,减少中断频率换微小延迟)、TCP zerocopy(sendfile、MSG_ZEROCOPY、splice)。io_uring 的网络支持让"批量提交收发"成为新范式。选型逻辑:通用服务用好内核栈(调优 + SO_REUSEPORT + epoll/io_uring)已可达数百万并发;极致 pps 或延迟才上 XDP/DPDK。

云网络的性能特性。VPC 内延迟:同 AZ 约 0.1-0.5ms(虚拟化交换),跨 AZ 约 1-2ms(同城机房),跨 Region 数十到数百 ms(光速 + 骨干网)——架构上"数据与计算同 AZ"是铁律,跨 AZ 调用链的每跳都加 1-2ms。带宽与 pps 限制:云实例的网络能力按规格档位(小规格可能只有 1-5Gbps 与数十万 pps),突发流量触发限流(丢包或整形)——实例内看不全,需云平台指标(如 AWS 的 network packets dropped)。负载均衡:L4(连接级分发,NLB)与 L7(请求级,ALB)的延迟税约 0.1-1ms,健康检查、连接排空(drain)、跨 zone 负载均衡(默认开启则每请求可能跨 AZ!)都是配置要点。NAT 网关:端口与连接数限制、每连接计费。DNS:解析延迟(首次 1-100ms)与缓存策略(JVM 的 DNS 缓存永久性问题、nscd/systemd-resolved)。容器网络(CNI):overlay(VXLAN 封装,每包多 50 字节与封装 CPU)vs 路由模式(Calico BGP,接近原生)vs eBPF 模式(Cilium kube-proxy replacement,iptables 的 O(n) 规则匹配换成 eBPF 的 O(1)——大集群必换,5000 服务的 iptables 规则同步要分钟级)。

网络观测工具矩阵。计数器:sar -n DEV/EDEV/TCP/ETCP(吞吐、错误、TCP 段与重传)、/proc/net/snmp 与 netstat(协议栈全量计数)、ip -s link(网卡级)。连接:ss -tin(每连接的 RTT/cwnd/重传/缓冲——单连接诊断之王)、conntrack -L(NAT 表)。事件追踪(eBPF):tcpretrans(重传归因)、tcplife(连接生命周期)、tcprtt(RTT 分布)、tcpstates(状态迁移)、tcpdrop(内核丢包点与调用栈——netstat 计数背后的"为什么")、tcptracer(连接事件)。抓包:tcpdump(终极真相,但高 pps 下自身丢包——-s 96 截断、-c 限量、BPF 过滤)、tshark 分析。栈内延迟:bpftrace 挂 kprobe 测各段耗时(如 netdev_start_xmit 到驱动的延迟)。方法论:计数器发现(重传率、溢出)→ ss/事件工具归因(哪条连接、哪个进程)→ tcpdump 定案(包级证据)→ 分层修复。网络排障的纪律是"两端同时抓":单端抓包无法区分"没发出去"与"发了没到",tcpdump 两端时间戳对齐是铁证。

核心概念

  • TCP
  • 拥塞控制
  • 重传
  • 网络栈
  • XDP

要点精析

RTT、带宽与 BDP:网络性能的物理框架。RTT(往返时延)由传播延迟(距离/光速,光纤中约 5μs/km)+ 传输延迟(包大小/带宽)+ 处理与排队延迟构成。BDP(带宽时延积)= 带宽 × RTT,是"填满管道所需的在途字节数":1Gbps × 50ms = 6.25MB。TCP 要跑满带宽,cwnd 与接收窗口都必须 ≥ BDP——这就是高带宽长距离传输需要调大 tcp_rmem/tcp_wmem 上限(默认 6MB 可能不够 10Gbps 跨洋)的原因。诊断工具:ss -tin 的 rtt:x/y 与 cwnd;iperf3 多流测试(单流受 cwnd 限制,多流聚合才逼近带宽上限——单流跑不满不等于带宽不足!)。延迟的不可压缩部分(光速)决定了架构:跨 Region 同步复制的写延迟下限 = RTT,共识协议(Raft/Paxos)的提交延迟 = 多数派 RTT——把"必须同步"的东西放在低 RTT 域内是分布式系统设计的第一原则。测量 RTT 用 ping(ICMP,可能被限速不代表 TCP 路径)或 tcping/hping3(TCP SYN 探测,更真实)、tcprtt(被动测量真实连接的 RTT 分布)。

重传的完整分类与诊断。重传率 = TcpRetransSegs / TcpOutSegs(netstat -s 或 nstat 差分),健康内网 <0.01%,公网 <0.5%,>1% 即显著问题。分类:(1)快速重传(3 dup ACK 触发,丢单个包,代价约 1 RTT + cwnd 减半);(2)RTO 超时重传(丢多个包或 ACK 全丢,指数退避 200ms/400ms/800ms——延迟毛刺的主犯,ss -tin 的 retrans 与 rto 字段可见);(3)TLP 尾丢包探测(最后一个包丢了没有 dup ACK,2×RTT 后探测——比 RTO 温和);(4)伪重传(包其实到了但 ACK 丢失/迟到,对端收到重复数据)。诊断链:tcpretrans 实时归因(哪个进程哪条连接)→ tcpdrop 看内核主动丢弃点(本机栈内丢包:缓冲满、netfilter、校验和错)→ 两端 tcpdump 对比(区分本机没发、路径丢、对端没回)→ mtr/traceroute 定位路径跳(某跳持续丢包且后续跳也丢 = 该跳问题;只有该跳丢 = ICMP 限速,无害)。云环境加查:实例网络指标(packets dropped)、安全组/ACL、NAT 网关端口。修复对应:路径丢包找运营商/云商;本机缓冲溢出调 rmem/wmem 与 backlog;对端处理慢优化对端应用。

TCP 连接建立与关闭的性能细节。建连成本:1 RTT 握手 + TLS 再加 1-2 RTT(TLS1.3 是 1-RTT,会话恢复 0-RTT)——HTTPS 跨洋首字节时间的大头。SYN 重传:SYN 丢失后 1s、2s、4s 指数退避(tcp_syn_retries 默认 6 次约 127 秒才放弃)——客户端"连接超时 3 秒"的实现通常是应用层超时而非 TCP 层。accept 队列(全连接队列):min(backlog 参数, somaxconn) 为上限,满了默认丢 ACK(对端以为建连成功开始发数据,实际被丢——诡异的应用层超时),tcp_abort_on_overflow=1 则发 RST(快速失败,语义更诚实);观测:ss -lnt 的 Recv-Q(当前队列长度)对比 Send-Q(上限),netstat -s 的 overflowed 计数。关闭:FIN 四次挥手 + TIME_WAIT(主动关闭方,2MSL=60s)——高并发短连接下 TIME_WAIT 堆积的正确解法是连接复用(HTTP keepalive、数据库连接池、gRPC 长连接),调 ip_local_port_range 扩端口与 tcp_tw_reuse(仅对客户端出方向连接安全)是缓解。CLOSE_WAIT 堆积则是应用 bug(收到 FIN 没调 close)——ss -s 的状态统计是体检入口。

拥塞控制算法的行为与选择。cubic(默认):窗口按时间的三次函数增长,丢包后乘性减小(0.7 倍),对丢包敏感(浅缓冲设备上丢包即减速),高 BDP 路径爬升慢。bbr:不依赖丢包信号,持续探测瓶颈带宽(BtlBw)与最小 RTT(RTprop),窗口 = BtlBw × RTprop——在有随机丢包的路径(无线、跨洋)与浅缓冲路径上吞吐显著优于 cubic;争议:与 cubic 混跑时 bbr 可能抢占更多带宽(公平性问题),且多 bbr 流之间也有争抢;bbrv2/v3 改进公平性与丢包响应。dctcp(数据中心 TCP):依赖 ECN 标记(交换机浅缓冲 + ECN),窗口按标记比例精细调整,配合低延迟交换网络实现数据中心内 μs 级公平共享——需要全网 ECN 支持。选择:通用互联网 cubic(默认稳妥);已知路径有随机丢包或需要单流高吞吐(大文件分发、跨洋复制)试 bbr(sysctl net.ipv4.tcp_congestion_control=bbr,需内核模块);数据中心内部 dctcp(需交换机 ECN)。验证方法:iperf3 对比 + 真实流量灰度,观察吞吐、P99 延迟、重传率三指标——拥塞控制的收益高度依赖路径特征,没有普适最优。

网卡与驱动层的性能要素。多队列(RSS):网卡按包哈希(四元组)分发到 N 个硬件队列,每队列独立中断——单核收包瓶颈(约 50-100 万 pps)的解法,队列数应 ≈ 核数且中断分散绑核(irqbalance 或手动)。RPS/RFS:网卡不支持多队列时的软件方案(RPS 按哈希把包投递到其他 CPU 的 backlog 队列,RFS 进一步让包流向应用所在 CPU——缓存亲和);XPS 是发送方向的对应物。中断合并(ethtool -c):rx-usecs/rx-frames 控制"攒多少包或多少微秒才中断"——高吞吐场景加大合并省 CPU,低延迟场景减小合并(或开 adaptive 模式)。GRO/LRO:收方向把同流的小包合并为大包再上栈(每包协议处理成本摊薄,CPU 省 30-50%),LRO 是硬件版(有正确性风险,转发场景禁用)。offload 家族:checksum(校验和卸载)、TSO/GSO(分段卸载——TCP 分段交给网卡/软件延迟到最后一刻)、VLAN offload——ethtool -k 查看。观测网卡瓶颈:/proc/net/softnet_stat(dropped/time_squeeze)、ethtool -S(网卡级计数:rx_missed_errors 是 ring buffer 满、rx_no_buffer 是硬件缓冲不足——调 ethtool -G 加大 ring)、sar -n DEV 对比规格上限。

netfilter/iptables 的性能税与 eBPF 替代。iptables 规则按链线性匹配:规则数 O(n),每包遍历——Kubernetes 每个 Service/Endpoint 生成规则,5000 服务 × 多端点 = 数万条规则,每包匹配成本与规则同步时间(分钟级!)都成为瓶颈;conntrack 连接跟踪表(nf_conntrack)为每连接维护状态:表满即丢包(dmesg: table full)、每包哈希查找与锁争用(高 pps 下 conntrack 是著名瓶颈)。观测:iptables -L -n | wc -l(规则数)、conntrack -C 对比 sysctl net.netfilter.nf_conntrack_max、perf 中 nf_hook_slow/ipt_do_table 的占比。替代方案:nftables(集合与映射结构,O(1) 查找,规则原子替换);ipvs(L4 负载均衡的哈希表实现,K8s 的 kube-proxy ipvs 模式);eBPF(Cilium 的 kube-proxy replacement:socket 层直接做服务翻译,包不过 netfilter——大集群标配,延迟与 CPU 双降)。诊断技巧:iptables -nvL 的计数器(每规则命中包数)定位热规则;trace 目标(iptables -t raw -A PREROUTING -p tcp --dport 80 -j TRACE + dmesg)看单包的全链路径。

DNS:最常被忽视的延迟源。一次未命中缓存的解析 = 1-2 个 RTT(递归服务器)到数十 ms(多级查询),且默认 UDP 53 的超时重传是 5 秒起(resolv.conf 的 timeout/attempts)——DNS 服务器抖动时应用表现为"偶发 5 秒卡顿"(经典的 resolv.conf 重试语义)。陷阱清单:(1)JVM 默认永久缓存成功解析(networkaddress.cache.ttl=-1 在有 SecurityManager 时),IP 变更后不感知——生产必设 TTL(如 30-60 秒);(2)glibc 的 getaddrinfo 串行查询 A 与 AAAA(IPv6 不通时每次白等)——单栈环境禁用 AAAA 或用 happy eyeballs;(3)容器内 /etc/resolv.conf 的 ndots:5(K8s 默认)导致外部域名先尝试 5 个 search 域后缀(每次 NXDOMAIN 一个 RTT)——外部调用多的服务加尾点(example.com.)或调 ndots;(4)conntrack 竞态丢 UDP DNS 包(K8s 著名问题,5.10 前内核的 insert_failed,解法:NodeLocal DNSCache 或 TCP DNS)。观测:bpftrace 挂 udp_sendmsg 统计 53 端口流量、resolvconf 抓包(tcpdump port 53)、应用侧解析延迟打点、systemd-resolved 的 statistics。

网络延迟的分层测量方法。端到端延迟 = 应用处理 + 主机栈(发送侧)+ 网络路径 + 对端主机栈 + 对端应用处理。分层工具:(1)路径层——ping/mtr(ICMP,注意限速失真)、tcprtt(真实连接 RTT 分布)、traceroute(逐跳,中间跳超时不代表丢包);(2)主机栈层——bpftrace 测关键函数延迟(tcp_sendmsg 到 ip_queue_xmit 到 dev_queue_xmit 的各段)、sockperf(回环或双机测栈的固有延迟,μs 级精度)、/proc/net/softnet_stat 的 time_squeeze(收包处理积压);(3)socket 缓冲层——ss -tm 的 skmem(缓冲占用满 = 反压)、tcpdump 对比"应用 write 时刻"与"包上线时刻"(栈内延迟);(4)应用层——请求日志时间戳分解(收到请求 → 处理完 → write 返回)。经典案例:应用 P99 高但 ping 正常——tcpdump 发现包早在 1ms 内到达网卡,但应用 50ms 后才 read:事件循环阻塞或线程池饱和(第 5 章问题伪装成网络问题)。方法论:延迟归因必须"时间戳对齐"——同一请求在各层的进出时刻拼成时间线,哪段宽哪段是瓶颈。

高并发服务器的网络架构模式。SO_REUSEPORT 多进程/多线程:N 个 listener 绑同端口,内核按四元组哈希分发连接——消除单 accept 队列锁争用,nginx worker、HAProxy threads、Envoy listener 的基础;注意:进程重启时其队列中的连接被 RST(优雅重启需要 drain 配合)。事件驱动(epoll + 非阻塞):单线程万级连接(Redis、nginx 模型),CPU 效率最高但单核上限(配合 REUSEPORT 横向扩展);io_uring 的多 shot accept 与批量收发是下一代形态。线程每连接(传统 Apache prefork、数据库连接):千级连接上限,但编程模型简单——连接数可控的内部服务仍合理。零拷贝路径:sendfile(静态文件,页缓存直达网卡)、splice(管道桥接)、MSG_ZEROCOPY(应用缓冲直发,需处理完成通知——大缓冲高带宽才划算)。负载均衡器架构:L4 DSR(直接服务器返回,回程不过 LB——大流量下载场景)、L7 代理(终结 TCP + HTTP 语义,可做重试/熔断/压缩——服务网格 sidecar 的形态,每跳加 0.2-1ms 的代理税)。选型由连接规模、延迟预算与功能需求决定。

云网络的性能清单。实例网络规格:按 vCPU 档位分配带宽与 pps 上限(如 8 vCPU 实例 5Gbps/50 万 pps),超限触发限流——云平台指标(bw_in/out_drop、pps_drop)是实例内看不到的关键数据;突发型实例的网络基线更低。跨 AZ/Region:同 AZ <0.5ms、跨 AZ 1-2ms(部分云 2-3ms)、跨 Region 按距离——微服务调用链每跳跨 AZ 都加税,K8s 的 topology aware routing(就近路由)与云 LB 的 cross-zone load balancing 开关(关闭可省跨 AZ 但牺牲均衡性)是架构决策点。托管服务的连接语义:NLB(L4,透明转发,保留客户端 IP,idle timeout 350s——长连接需要 keepalive 心跳防静默断开)、ALB(L7,idle 60s)、NAT 网关(端口耗尽:每 NAT IP 64K 端口 × 目标四元组,爬虫/出方向高并发场景要监控 SNAT 端口分配失败)。VPC 的 MTU:多数云支持 9000 jumbo frame(VPC 内),跨 Region/公网回落 1500——MSS 钳制(TCP MSS clamping)不当会造成"能 ping 不能建连"的 PMTU 黑洞(ICMP 被过滤),诊断用 ping -M do -s 1472 逐段试探。

抓包(tcpdump)的工程实践。tcpdump 是网络的"终极真相"但自身有陷阱:(1)高 pps 丢包——输出中的 "X packets dropped by kernel" 是抓包缓冲满(-B 加大缓冲、-s 96 截断包头、-c 限量、BPF 过滤精确化:tcpdump -i eth0 'tcp port 80 and host 10.0.0.5');(2)抓包点位置——tcpdump 在协议栈的 AF_PACKET 钩子:发送方向抓到 = 已交给驱动(不含网卡硬件失败),接收方向抓到 = 已过驱动(不含 ring buffer 丢弃,那要看 ethtool -S);(3)时间戳精度——默认内核时间戳(软中断处理时刻),硬件时间戳(ethtool -T 支持时)更准;(4)两端对齐——单端抓包只能证明"我这侧的行为",争议问题(重传是谁的锅)必须两端同时抓 + NTP 时钟同步。分析工具:tshark 的统计(tshark -r cap.pcap -q -z io,stat,1 -z conv,tcp -z expert——专家系统直接标注重传/乱序/零窗口事件)、wireshark GUI 的流追踪(Follow TCP Stream)。经典判读模式:dup ACK 风暴 + 快速重传 = 路径丢包;零窗口([TCP ZeroWindow])= 接收端应用读不动(不是网络问题!);SYN 重传无 SYN-ACK = 对端 backlog 满或防火墙丢包;RST 紧随数据 = 对端进程已死或队列溢出 abort。

网络性能问题的模式库与速查。模式一:延迟毛刺 + 重传——tcpretrans/tcpdrop 归因,两端抓包定界(路径 vs 主机)。模式二:吞吐上不去——单流 cwnd 受限(BDP 计算 + ss 看 cwnd)vs 带宽受限(iperf3 多流验证)vs CPU 受限(softnet_stat 的 squeeze、单核 %soft 100%)。模式三:连接失败——SYN 无响应(防火墙/backlog)vs SYN-ACK 后 RST(accept 队列溢出 + abort_on_overflow)vs 端口耗尽(TIME_WAIT 海量 + ip_local_port_range)。模式四:偶发 5 秒卡顿——DNS 超时重试(resolv.conf 语义)或 ARP 刷新或 conntrack 表满。模式五:容器网络慢——CNI 模式(overlay 封装税)、kube-proxy iptables 规则数、MTU 不匹配(封装后超 MTU 触发分片或 PMTU 黑洞)。模式六:长连接静默断开——云 LB idle timeout(无流量被清理,应用无感知,下次写才发现)——keepalive 间隔必须小于 LB timeout(tcp_keepalive_time 默认 7200 秒远大于 LB 的 60-350 秒,必调)。每个模式配"确认指标 + 工具 + 处置",构成网络排障决策树。

代码示例

ss -s
/* 连接状态总览:TIME_WAIT/CLOSE_WAIT 堆积体检 */
ss -tin dst 10.0.0.5
/* 指定对端的连接详情:rtt cwnd retrans skmem */
ss -lnt
/* 监听 socket:Recv-Q(当前 accept 队列) Send-Q(backlog 上限) */
ss -tan state time-wait | wc -l
nstat -az | egrep "TcpRetransSegs|TcpExtListenOverflows|TcpExtTCPBacklogDrop|TcpActiveOpens|TcpPassiveOpens"
netstat -s | egrep -i "retrans|overflow|prune|collapse"
连接与协议栈状态的速查组合。ss -s 一眼体检:timewait 数万 = 短连接风暴(查连接复用);closewait 堆积 = 应用没 close(fd 泄漏前兆);estab 数量对比容量规划。ss -tin 是单连接显微镜:rtt:0.5/0.2(RTT 与方差 ms)、cwnd:10(拥塞窗口,MSS 倍数——跨洋大流量连接 cwnd 应爬到数百上千,长期 10 说明持续丢包或响应太小)、retrans:0/15(当前/累计重传)、skmem 的 rb/tb(接收/发送缓冲占用——tb 持续满 = 对端收不动或路径窄,rb 满 = 本端应用读太慢,零窗口的前兆)。ss -lnt 的 Recv-Q 对监听 socket 语义特殊:不是数据而是 accept 队列当前长度,持续接近 Send-Q(backlog)即溢出边缘——配合 nstat 的 TcpExtListenOverflows(累计溢出次数,增长中 = 正在丢连接,调 backlog/somaxconn 或查应用 accept 速度)。nstat -az 输出原始计数器(比 netstat -s 的文本稳定,适合脚本差分):TcpRetransSegs/TcpOutSegs 比值 = 重传率;TCPBacklogDrop = socket 缓冲满丢包;netstat -s 的 prune/collapse 行 = 接收缓冲压力下的强制清理(应用读慢的证据)。这组命令 10 秒完成"连接层体检",是网络问题分诊的第一站。
sar -n DEV 1
sar -n EDEV 1
sar -n TCP,ETCP 1
ip -s link show eth0
ethtool -S eth0 | egrep -i "drop|miss|error|no_buffer"
ethtool -i eth0; ethtool eth0
cat /proc/net/softnet_stat
/* 每行一个 CPU:处理包数 dropped time_squeeze ... */
网卡与驱动层的观测组合。sar -n DEV 的 rxkB/s、txkB/s 对比 ethtool eth0 的速率(如 10000Mb/s = 1.25GB/s)得带宽利用率;rxpck/s 是 pps——小包负载(64B)下 1M pps 只有 0.5Gbps 但 CPU 成本巨大,pps 与带宽要分开看。sar -n EDEV 的 rxdrop/s、txdrop/s 是驱动层丢弃。ip -s link 的 errors/dropped/overrun 是软件栈计数。ethtool -S 是网卡硬件计数(名称因驱动而异):rx_missed_errors/rx_no_buffer_count = 网卡 ring buffer 满(CPU 收不过来——ethtool -G 加大 ring、检查中断分布与软中断 CPU);rx_crc_errors = 物理层问题(线缆/光模块)。/proc/net/softnet_stat 是收包路径的核心证据:第 1 列 processed(处理包数)、第 2 列 dropped(netdev_budget 内没处理完被丢——调大 net.core.netdev_max_backlog)、第 3 列 time_squeeze(软中断预算耗尽次数——调大 netdev_budget/netdev_budget_usecs 或检查单核软中断饱和);每行对应一个 CPU,全零是健康态。判读链:带宽高 + 无 drop = 正常繁忙;pps 高 + time_squeeze 增长 = 收包 CPU 瓶颈(RSS 多队列 + 绑核 + GRO);rx_missed 增长 = 硬件缓冲不足(ring 加大 + 中断合并调整)。
/usr/share/bcc/tools/tcpretrans
/usr/share/bcc/tools/tcpdrop
/* 内核丢包点 + 调用栈:比计数器更深的归因 */
/usr/share/bcc/tools/tcplife
/usr/share/bcc/tools/tcprtt -i 1
/usr/share/bcc/tools/tcpstates
/* 连接状态迁移时间线:SYN_SENT 停留久 = 建连问题 */
TCP 事件的 eBPF 追踪五件套。tcpretrans 打印每个重传(含 TLP 标记):PID、四元组、序列号——归因到进程与连接,配合时间戳可与应用延迟毛刺对齐。tcpdrop 更进一步:追踪内核主动丢弃 skb 的位置(kprobe tcp_drop)并打印调用栈——区分"缓冲满丢"(sk_stream_kill_queues 栈)、"netfilter 丢"(nf_hook 栈)、"校验和错"等,回答 netstat 计数器背后的"在哪丢的、为什么"。tcplife 记录连接全生命周期(建立→关闭:存活时长、收发字节)——短连接风暴(大量 <1s 连接)、异常大流量连接的发现工具;比 conntrack 轻量且不依赖 NAT 模块。tcprtt 被动聚合真实连接的 RTT 直方图(按目标端口/地址可过滤)——"网络路径延迟到底多少"的持续观测,对比应用延迟可分离网络与应用贡献。tcpstates 输出状态迁移事件(LISTEN→SYN_SENT→ESTABLISHED→...,含每状态停留时长)——SYN_SENT 停留数秒 = 对端不回 SYN-ACK(防火墙/backlog),CLOSE_WAIT 停留 = 应用不 close,FIN_WAIT 堆积 = 对端不确认关闭。五工具全部基于 tracepoint/kprobe,事件级开销(低频事件下 <1%),生产可短时挂载。组合剧本:应用报延迟毛刺 → tcprtt 排除路径 RTT 恶化 → tcpretrans 抓重传事件对齐毛刺时刻 → tcpdrop 看是否本机栈内丢 → 两端 tcpdump 定案。
tcpdump -i eth0 -s 96 -c 5000 -w /tmp/cap.pcap "tcp port 80 and host 10.0.0.5"
tshark -r /tmp/cap.pcap -q -z io,stat,1
tshark -r /tmp/cap.pcap -q -z conv,tcp
tshark -r /tmp/cap.pcap -q -z expert
/* 专家信息:重传、乱序、零窗口、dup ACK 自动标注 */
tshark -r /tmp/cap.pcap -Y "tcp.analysis.retransmission" -T fields -e frame.time -e ip.src -e ip.dst
抓包与离线分析的标准流程。tcpdump 参数的工程考量:-s 96 只抓包头(96 字节足够 TCP/IP 全头 + 少量载荷,缓冲效率提升 10 倍以上,高 pps 下减少自身丢包);-c 5000 限量(防磁盘写满);-w 写文件(不解析不打印,最低开销);BPF 过滤表达式在内核态执行(抓包前过滤,比抓全量再筛高效得多——表达式语法:host/net/port/and/or/not,注意 tcpdump 的过滤不计入 VLAN/隧道封装时要用 vlan 关键字)。tshark 离线分析三板斧:-z io,stat,1 按秒统计包数/字节数(流量形态与毛刺时刻);-z conv,tcp 会话表(每连接的包数、字节、时长——找异常连接);-z expert 是自动化判读(把重传、fast retransmission、out-of-order、dup ack、zero window、window full 等事件分级列出——80% 的问题 expert 直接给答案)。-Y 是显示过滤器(读文件时用,语法丰富:tcp.analysis.flags、http.response.code 等),-T fields -e 提取字段做脚本化统计。零窗口(tcp.window_size == 0)的判读要点:它指接收方缓冲满、应用读取慢——是应用性能问题而非网络问题,接下来应查接收端的 CPU/线程/事件循环(第 5 章工具)。
sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmem net.core.rmem_max net.core.wmem_max
sysctl net.ipv4.tcp_congestion_control net.ipv4.tcp_available_congestion_control
sysctl net.core.netdev_max_backlog net.core.netdev_budget net.core.somaxconn
sysctl net.ipv4.tcp_slow_start_after_idle net.ipv4.tcp_mtu_probing net.ipv4.tcp_keepalive_time
sysctl net.netfilter.nf_conntrack_max net.netfilter.nf_conntrack_buckets 2>/dev/null
ethtool -c eth0
/* 中断合并参数 */
ethtool -l eth0
/* 多队列通道数 */
网络内核参数的审查清单(静态分析)。tcp_rmem/tcp_wmem 三元组(min/default/max):自动调优的范围,max 默认 6MB——高 BDP 路径(10Gbps × 50ms = 62MB)需调大到 64MB+,同时 rmem_max/wmem_max(setsockopt 上限)要同步。tcp_congestion_control:默认 cubic,available 列出可切换项(bbr 需加载模块:modprobe tcp_bbr)。netdev_max_backlog:非 NAPI 路径的每 CPU 积压上限(RPS 场景重要,默认 1000 常不够,高 pps 调 10000+,配合 softnet_stat 第 2 列验证)。netdev_budget/budget_usecs:软中断单次轮询的包数/时间预算(time_squeeze 增长时调大,代价是延迟其他软中断)。somaxconn:accept 队列上限(默认 4096/128 视版本,高并发服务与应用 backlog 参数取小者生效——两边都要调)。tcp_slow_start_after_idle:空闲连接重新慢启动(长连接池的突发请求被惩罚——L7 代理与 RPC 场景常设 0)。tcp_mtu_probing:PMTU 黑洞的自救(ICMP 被过滤时按 MSS 探测,设 1)。tcp_keepalive_time:默认 7200 秒——云 LB idle timeout 之前必须发心跳(调 300-600 或应用层心跳)。conntrack_max/buckets:NAT/防火墙场景的表容量(表满丢包,max 按并发连接数 ×2,buckets 旧内核需手动设为 max/4 优化查找)。ethtool -c/-l:中断合并与队列数的硬件侧配置。所有调整遵循单变量 + 基准验证纪律。

实验笔记

实验九:网络栈观测与 TCP 行为实测。

实验目标:(1)实测 RTT、BDP 与拥塞窗口的关系,验证慢启动行为;(2)用 tcpretrans/tcpdrop/ss/tcpdump 四工具完成一次丢包问题的完整归因;(3)复现 accept 队列溢出与 TIME_WAIT 端口耗尽两个高并发经典问题;(4)测量内核栈的固有延迟与 GRO/中断合并的影响。

环境准备:两台 Linux 虚拟机(同宿主机或同 VPC,记为 A/B),安装 iperf3、sysstat、BCC tools、tshark、sockperf(可选)。确认基础 RTT:ping 记录 A→B 的 RTT 基线(同宿主机应 <0.5ms)。所有实验记录 sysctl 原值以便恢复。

步骤一(BDP 与慢启动实测):A 向 B 传大文件(iperf3 -t 30 或 scp 1GB),期间在 A 上每秒执行 ss -tin dst B_IP 记录 cwnd 变化——应观察到慢启动的指数爬升(cwnd 10→20→40→...每 RTT 翻倍)到饱和或丢包回落。计算验证:稳态吞吐 ≈ cwnd × MSS / RTT(用 ss 的 cwnd 与 ping 的 RTT 代入,对比 iperf3 实测带宽)。进阶:用 tc netem 给 B 方向注入 20ms 延迟(tc qdisc add dev eth0 root netem delay 20ms),重测——吞吐应下降(BDP 不变但爬升时间变长)或需要更大缓冲;把 net.ipv4.tcp_wmem 的 max 调小到 64KB 重测,验证窗口上限对吞吐的钳制(64KB/20ms = 26Mbps 上限——亲手验证 BDP 公式)。

步骤二(丢包归因全流程):B 上起 iperf3 服务端,A 打压的同时用 tc netem 在 A 的出口注入 0.5% 丢包(tc qdisc change dev eth0 root netem loss 0.5%)。观测链:(1)nstat 差分 TcpRetransSegs 确认重传率上升;(2)tcpretrans 实时捕获重传事件(应看到 A→B 的流);(3)ss -tin 观察 cwnd 的锯齿(丢包减半再爬升)与 retrans 计数;(4)A 端 tcpdump -s 96 -c 2000 -w a.pcap "host B_IP",B 端同时抓 b.pcap,tshark -z expert 分析:A 侧看到重传标记,B 侧看到 dup ACK——两端证据拼合确认"路径丢包"而非主机丢包;(5)tcpdrop 应无输出(本机栈没丢)——反证丢包在 netem(路径模拟)。做完删除 netem(tc qdisc del dev eth0 root),重测确认恢复。这个实验把"重传归因"的完整证据链走了一遍。

步骤三(accept 队列溢出复现):B 上起一个"慢 accept"服务(python 单线程 HTTP 服务,处理每请求 sleep 1 秒),先 sysctl -w net.core.somaxconn=10。A 上用 wrk -c 50 -t 4 打压。观测:B 的 ss -lnt 中该端口 Recv-Q 涨到 10(Send-Q 也是 10——队列满);nstat 的 TcpExtListenOverflows 与 ListenDrops 持续增长;A 侧 wrk 报超时。修复验证:somaxconn 与应用 backlog 调大到 1024 后重测(溢出消失但延迟仍在——队列只是缓冲,处理能力才是根本,引出第 12 章调优的"扩容 vs 优化"之辨)。

步骤四(TIME_WAIT 与端口耗尽):A 上写脚本对 B 的 HTTP 服务发起 3 万个短连接(每连接请求一次即关,bash 循环 curl 或 python)。观测:ss -s 的 timewait 数攀升;ss -tan state time-wait | wc -l 计数;当 TIME_WAIT 逼近 ip_local_port_range 的端口数(cat /proc/sys/net/ipv4/ip_local_port_range,默认约 28K)时,新连接开始失败(cannot assign requested address)。解法验证:改用连接复用(python requests.Session 或 curl 的 keepalive)重跑 3 万请求,TIME_WAIT 应只有个位数。记录两种模式的耗时对比(连接复用的吞吐优势通常 5-10 倍)。

步骤五(栈延迟与 offload 影响,可选进阶):用 sockperf ping-pong(B 起 sockperf server,A 跑 sockperf ping-pong -i B_IP)测 A-B 栈固有延迟基线(虚拟机通常 20-100μs)。在 B 上 ethtool -K eth0 gro off 关闭 GRO、ethtool -C eth0 rx-usecs 0 关闭中断合并,重测延迟与相同流量下的 CPU(mpstat %soft)——量化 offload 与中断合并的"CPU 换延迟"权衡。恢复设置。

预期结果:cwnd 爬升曲线与 BDP 验证计算、丢包归因的五步证据链记录(含两端 pcap 的 expert 分析)、队列溢出与端口耗尽的复现数据与修复对比、(可选)offload 参数对延迟/CPU 的影响表。

进阶挑战:(1)把拥塞控制切到 bbr(modprobe tcp_bbr + sysctl),在注入 1% 丢包的路径上对比 cubic 与 bbr 的吞吐与延迟,验证 bbr 对丢包的鲁棒性;(2)用 tcprtt 持续观测实验全程的 RTT 分布,分析 netem 注入前后直方图的位移;(3)复现 DNS 5 秒卡顿:把 resolv.conf 指向不存在的服务器,测量 getaddrinfo 的耗时(timeout × attempts 语义),再对比 systemd-resolved 缓存命中的耗时;(4)用 bpftrace 写一个测量 tcp_sendmsg 到 dev_queue_xmit 栈内延迟的脚本,观察高负载下主机栈延迟的膨胀。

延伸阅读

  • 《Systems Performance》第 2 版第 10 章 Networks 原文。原书覆盖网络栈全景(从 socket 到网卡)、TCP/IP 性能机制、观测工具与云网络,附大量 Linux/Solaris 工具判读示例。特别推荐其中"网络观测的层次模型"(应用/socket/协议栈/驱动/物理)与延迟分解方法,是本章笔记的结构来源。
  • 《TCP/IP Illustrated, Volume 1》第 2 版(Fall & Stevens)。TCP/IP 协议的权威教材:握手与挥手状态机、窗口管理(滑动窗口、零窗口探测)、拥塞控制(慢启动/拥塞避免/快速恢复的原始论述)、Nagle 与延迟 ACK 的交互、TIME_WAIT 的存在理由。读协议细节时以本书为准,博客与 man page 常有讹传。配合《Illustrated》第 2 卷(TCP 实现)理解 Linux 具体行为。
  • Brendan Gregg 的网络分析博文与工具页:《Linux tcpretrans》《tcpdrop》《TCPlife》等 BCC 工具文档(含真实案例输出判读),以及他的网络火焰图探索文章。Cloudflare 与 Netflix 的技术博客也有大量 eBPF 网络观测实战(如 Cloudflare 的 XDP DDoS 防御系列、Netflix 的 VPC 网络性能分析),是本章工具在生产规模下的应用范例。
  • 《High Performance Browser Networking》(Ilya Grigorik,免费在线 O'Reilly 书,hpbn.co)。从应用视角讲网络性能:TCP/TLS/HTTP 各层的延迟构成与优化(连接复用、队头阻塞、HTTP/2 多路复用、QUIC/HTTP3 的动机)、无线网络的特性、WebRTC。虽然面向浏览器,其中"协议机制 → 应用性能"的分析方法对服务端工程师同样适用,是理解"为什么连接复用是铁律"的最佳读物。
  • Linux 内核文档 Documentation/networking/(ip-sysctl.rst 是全部网络 sysctl 的权威语义、scaling.rst 讲 RSS/RPS/RFS/XPS、napi 与 qdisc 文档)与 iproute2 的 man 页(ss、tc、ip)。调参前查 ip-sysctl.rst 确认语义(如 tcp_tw_reuse 的适用边界、somaxconn 与 backlog 的取小关系),网络参数的网上 misinformation 极多,内核文档是唯一可靠来源。
  • Cilium 与 eBPF 网络文档(docs.cilium.io 的 eBPF 章节),以及 libbpf-tools 的网络工具集。Kubernetes 环境的网络性能必读:kube-proxy 的 iptables/ipvs/eBPF 三模式对比、conntrack 的规模问题、CNI 数据路径(overlay vs 路由 vs eBPF 原生)。即使不用 Cilium,其对内核网络数据路径的图解也是理解容器网络税的优秀教材。
  • 《Network Algorithmics》(George Varghese)与 DPDK/XDP 的官方文档。高性能网络的进阶路径:Varghese 的书讲"网络系统的算法级优化"(批处理、缓存友好、避免复制的通用原则);DPDK 文档(doc.dpdk.org)与 xdp-tutorial(github.com/xdp-project/xdp-tutorial)分别是内核旁路与内核内 eBPF 快路径的入门实战。当你的场景达到百万 pps 或 μs 级延迟需求时,这些是必经之路。
  • APNIC 与 RIPE 的技术博客(blog.apnic.net、labs.ripe.net),以及 NANOG 会议演讲。运营级网络问题的案例库:PMTU 黑洞、BGP 路由泄漏对延迟的影响、IPv6 过渡机制的性能陷阱、DNS 的全球部署实践。云与分布式系统的"网络玄学"问题(偶发丢包、跨域抖动)常需要运营商视角的知识才能定界,这些渠道是一线经验的聚集地。
9/13网络
0%