资讯动态

网络协议梳理实战:从分层模型到MTU/MSS与抓包排障

发布时间:2026/10/27 7:32:54 来源:尧图企业网站定制
网络协议梳理这件事很多人第一反应是这有什么好梳理的四层模型、七层模型书上写得明明白白。我早年也这么想直到某次线上接口成片超时前端说请求没到后端说没收到包网络组说链路是通的三方各自拿着自己的日志都说自己没问题。最后抓到包才发现是中间一段隧道的 MTU 比预想的小大包被丢了小请求正常、大请求超时症状看起来像偶发,其实是必然。那次之后我养成了一个习惯不管接手什么系统先做一遍网络协议梳理把这条链路上每一层跑的什么协议、谁在改包、谁在丢包用一张表说清楚。这篇就把我这套梳理办法摊开讲讲包括分层怎么切、参数怎么算、抓包怎么看、坑都在哪儿。1. 先把协议栈的骨架立起来1.1 按数据流向梳理别按协议名背清单我见过不少人的协议梳理是一张名词表以太网、ARP、IP、ICMP、TCP、UDP、DNS、HTTP、TLS一列排下来看着挺全真出事一个都用不上。原因很简单协议名是静态名词排障需要的是动态路径——你得知道一个字节从应用缓冲区出来之后先被谁套了一层头再被谁改了一次最后在哪个环节可能被扔掉。所以我做梳理的第一个动作永远不是列协议而是画一条数据流客户端应用 → 本机协议栈 → 网卡 → 交换机 → 路由器/网关 → 中间设备 → 服务端网关 → 反向代理 → 应用进程。这条线上的每一跳我都会问三个问题这一跳处理的是哪一层这一跳会不会改写报文这一跳出问题时的表现长什么样三问答完协议自然就挂上去了而且挂上去的是有用的协议不是词典里的协议。顺着这条思路我把整个协议栈拆成三条主线后面所有细节都往这三条线上挂寻址线我从哪来、到哪去。以太网 MAC、ARP、IP 地址、子网掩码、路由表、NAT 转换全归这条线。这条线断了表现是根本不通。连接线两端能不能对上话。TCP 握手、端口、监听队列、TLS 握手、DNS 解析全归这条线。这条线断了表现是连不上或连得慢。可靠性线话有没有说完整。序列号、确认号、重传、窗口、拥塞控制、分片重组全归这条线。这条线断了表现最恶心——通但慢或者偶尔失败。这三条线的好处是你拿到任何一个故障现象都能先落到某条线上再往下钻具体协议而不是从协议清单的第一行开始一个个排除。下面这张表是我自己常用的对照表故障现象和层级的映射基本都在里面现象大概率所在层首先怀疑的协议/机制第一手工具完全 ping 不通链路层 / 网络层ARP、VLAN、路由、安全组ip neigh、ip route能 ping 通但端口连不上传输层监听未起、防火墙、连接队列满ss -lntp、telnet连得上但很慢传输层 / 应用层窗口过小、重传、DNS 慢ss -ti、抓包小请求正常大请求超时网络层MTU / 分片 / PMTUDping -M do -s偶发 502 / 连接被重置传输层 / 代理层TIME_WAIT、keepalive、空闲超时ss -s、代理日志首字节慢但传输快应用层DNS、TLS 握手、慢查询curl -w分段计时1.2 一次请求到底被套了几层壳光说分层还是抽象我更愿意用套娃这个说法。你在应用层写下一个GET /api/user它出门的时候是这样的应用层先给它加上 HTTP 头变成一段文本TLS 层再给这段文本加上 5 字节记录头和加密后的负载TCP 层加 20 字节头不含选项带上源端口、目的端口、序列号IP 层加 20 字节头带上源 IP、目的 IP、TTL以太网层加 14 字节头带上源 MAC、目的 MAC尾部再加 4 字节 FCS 校验。每一层的开销我都建议你亲手算一遍因为很多带宽不够的错觉其实是头部开销吃掉的。以一个典型的 HTTPS 小请求为例HTTP 头假设 300 字节TLS 记录头 5 字节加 MAC/AEAD 标签 16 字节TCP 头 20 字节IP 头 20 字节以太网头 14 字节加 FCS 4 字节合计额外开销 79 字节。如果业务负载只有 200 字节那有效载荷占比不到 60%。这也解释了一个常见现象同一套服务改成 HTTPS 之后 QPS 掉了几个点不是加密算不动是每个包都变胖了包数量没变但总字节数涨了。反过来大文件传输时头部开销可以忽略真正卡脖子的是别的东西——窗口够不够大、有没有丢包触发拥塞控制降速、路径 MTU 是不是被某个隧道压小了。所以我在梳理时会按小包场景和大包场景分开看这两类问题的排查路径几乎不重叠混在一起想只会越想越乱。提示算头部开销的时候别忘了 TCP 选项。带上时间戳选项很多系统默认开TCP 头就是 32 字节而不是 20 字节MSS 也会跟着少 12 字节。抓包看到的tcp.len和你以为的不一样八成就是这个原因。1.3 梳理最终要产出三张能用的表我做一次完整的网络协议梳理最后会沉淀三样东西缺一样都算没做完。第一张是链路协议清单从客户端到服务端每一跳跑的什么协议、什么端口、谁维护、有没有加密。这张表看着朴素但新人接手时的价值极高很多这个端口为什么开着的疑问查表就有答案。第二张是关键参数表MTU、MSS、TCP 窗口、keepalive 时间、各类超时时间、DNS TTL、TLS 版本与套件。参数这东西最容易被忽略因为平时它们不出声一出事就是按秒计算的损失。我习惯把每一跳的 MTU 和超时都写进去后来救过我好几次。第三张是故障映射表就是上面那张现象对照表但要落成本系统的实际值。比如连不上对应到本系统是安全组没放行还是连接队列满了判断依据是什么、命令是什么。这张表是排障时的第一反应来源写一次能用很久。2. 链路层与网络层那些决定上限的细节2.1 MTU 1500 从哪来MSS 又该怎么算以太网默认 MTU 是 1500 字节这个数字不是什么物理定律是当年以太网规范定下的一个折中值——大到能摊薄头部开销小到冲突检测的时隙还能算得过来。它被沿用至今成了事实标准也成了无数大包不通故障的源头。MSS最大报文段长度是 TCP 层面的概念它等于 MTU 减去 IP 头和 TCP 头1500 - 20 - 20 1460。如果开了 TCP 时间戳选项就是1500 - 20 - 32 1448。这个 1460 就是你在抓包里正常情况下能看到的最大单个 TCP 段负载。一旦链路中间有一段 MTU 比 1500 小比如某些隧道封装会吃掉 20 到 60 字节或者 PPPoE 吃掉 8 字节那么一个 1460 字节的段到了那里就装不下要么分片要么被丢弃。分片这件事很坑。IPv4 允许中间路由器分片但很多设备出于性能考虑直接丢弃并回一个 ICMP 需要分片消息这就是路径 MTU 发现PMTUD机制。问题在于如果这个 ICMP 消息在中间被拦掉了现实里非常常见发送方永远收不到通知就会一直重传那个大包表现就是握手成功、小请求成功、一传大数据就卡死。这个现象有个专门的名字叫 PMTU 黑洞。我排查这类问题的标准动作是这样# 逐步加大包大小并禁止分片找到能通过的最大值 ping -M do -s 1472 目标IP # 1472 8(ICMP头) 20(IP头) 1500 ping -M do -s 1400 目标IP ping -M do -s 1200 目标IP能通的最大-s值加上 28 就是这条路径的实际 MTU。如果 1472 不通但 1400 通说明中间有东西把 MTU 压到了 1428 左右这时候要么在隧道的两端把 MSS 钳制下来iptables -t mangle -A FORWARD -p tcp --syn -j TCPMSS --clamp-mss-to-pmtu要么直接在应用层把单次写入的数据块控制在安全范围内。注意MSS 钳制只对 TCP 有效UDP 大包照样会碎。如果你的服务里有基于 UDP 的协议视频、DNS over UDP、自研的实时通信必须自己在应用层做分片别指望网络层帮你兜底。2.2 ARP 与邻居表同网段通信的隐形前置条件同一个网段内的两台机器通信光有 IP 是不够的报文最终得靠 MAC 地址在链路上送达。ARP 干的就是我知道你的 IP你的 MAC 是多少这件事。流程很简单广播问一句谁是 192.168.1.10对方单播回我是我的 MAC 是这个问的一方把结果缓存进邻居表。这个机制本身没什么好讲的但它在实际环境里制造的故障一点不少。容器化环境里尤其明显——如果容器网络的实现方式让 ARP 广播到不了该到的地方或者邻居表项老化得太快就会出现间歇性不通。表现是每隔一段时间有几秒钟的失败然后自己恢复日志里看不出任何异常。我常用的两个检查命令ip neigh show # 查邻居表状态看有没有 FAILED / INCOMPLETE arping -I eth0 192.168.1.10 # 主动探测看响应延迟和丢包邻居表里出现INCOMPLETE说明发了 ARP 请求但没收到回应这通常是二层隔离或者广播被限速。出现大量STALE是正常的内核会惰性刷新但如果一台机器上邻居表项数暴涨到几万条那就要检查是不是有扫描行为或者网段划分不合理。还有一个容易踩的坑多网卡机器上 ARP 响应的源地址选择。默认行为下内核可能用 A 网卡的地址去回应本该走 B 网卡的 ARP 请求导致对端学到的 MAC 和预期不符报文就跑到错误的网卡上去了。这个可以用arp_ignore和arp_announce两个参数来约束但改之前一定要在测试环境验证因为它们影响的是整机所有网卡的行为改错了会让原本正常的链路也出问题。2.3 IP 寻址、路由查找与 NAT 的副作用IP 层最核心的两个动作是查路由和改 TTL。查路由用的是最长前缀匹配——路由表里有10.0.0.0/8和10.1.0.0/16两条时去往10.1.2.3的包会走后者因为前缀更长更具体。这个规则听起来理所当然但实际出故障时经常有人搞混尤其是在有多条默认路由或者策略路由的环境里。我在梳理时会重点确认三件事默认路由指向哪里、有没有策略路由规则ip rule show、有没有多路径负载均衡。策略路由是最容易被遗忘的一块因为它不体现在常规的ip route输出里但一旦存在报文可能完全不走你预期的路径。有次排查一个部分请求走到了错误后端的问题最后发现就是一条遗留的策略路由规则按源端口取模分流把一小部分流量引到了老集群。NAT 则是另一类麻烦。SNAT 让内网机器共享出口地址代价是连接跟踪表conntrack成为有状态资源。表满了之后新连接直接建不起来报错是cannot allocate memory或者干脆静默丢包。检查方式sysctl net.netfilter.nf_conntrack_count sysctl net.netfilter.nf_conntrack_max conntrack -S # 看各状态的统计和插入失败计数conntrack -S里的insert_failed如果持续增长基本可以确定表满了或者有冲突。调大上限只是治标更该做的是看看到底是谁在制造大量短连接很多时候根源是某个服务的连接池配置不当或者有个定时任务在不停地建连断连。2.4 ICMP 不只是 ping 工具很多人把 ICMP 当成ping 用的实际上它是 IP 层的信使承载了大量诊断信息。目的不可达、TTL 超时、需要分片、参数错误这些消息都是 ICMP 送回来的。你把 ICMP 全禁掉换来的是网络变得哑——出问题没有任何反馈只能靠猜。TTL 超时消息是traceroute的实现基础它靠逐跳递增 TTL 来探测路径。如果中间设备不回 ICMPtraceroute就会显示一堆星号路径就断了线索。这种情况下可以换用基于 TCP 或 UDP 的探测方式很多现代实现都支持指定协议和端口绕过纯 ICMP 的限制。在梳理时我会特别记录一条这条链路上 ICMP 是被放行、限速还是丢弃。放行最好限速要记下限速阈值因为高并发探测时可能触发限速导致误判丢弃就得准备好替代方案比如提前在两端部署主动探测靠业务层面的心跳来判断连通性。3. 传输层参数、状态与那些算得出来的上限3.1 握手挥手状态机与实际观测TCP 建连三次握手、断连四次挥手这个流程谁都能背。但排障时真正有用的是状态机——每个状态代表什么、卡在哪个状态说明什么问题。我把常打交道的状态整理成下面这张表配的是我在实际环境里最常看到的成因状态含义卡在这里通常说明LISTEN服务端在监听正常SYN_SENT客户端已发 SYN等回应对端没收到或被防火墙丢弃SYN_RECV服务端收到 SYN 并回了 SYN-ACK半连接队列压力大或 SYN 被泛洪ESTABLISHED连接已建立正常FIN_WAIT_1主动关闭方发了 FIN对方迟迟不回 ACK可能应用卡住FIN_WAIT_2收到对方 ACK等对方 FIN对方应用没关连接典型资源泄漏CLOSE_WAIT被动关闭方收到 FIN尚未 close应用层没有正确关闭代码问题LAST_ACK被动方发了 FIN等最后 ACK短暂状态长期存在不正常TIME_WAIT主动关闭方等待 2MSL正常量大需关注端口CLOSE_WAIT是我最关注的一个。它长期大量存在几乎百分之百是应用代码的问题——收到了对方的关闭请求但没有调用 close 释放。这个状态不受任何内核参数影响调参数是白费力气只能改代码。而TIME_WAIT完全相反它是主动关闭方的正常状态量大是结果不是原因去消灭它往往会把问题变复杂。关于队列还有两个参数值得记半连接队列tcp_max_syn_backlog和全连接队列somaxconn与 listen 的 backlog 取小值。全连接队列溢出时内核默认行为是静默丢弃第三次握手的 ACK客户端会以为连接建立成功然后发数据收不到回应表现极其诡异。检查方式是看ss -lnt输出里的Send-Q列以及netstat -s | grep -i listen里的溢出计数。3.2 窗口、带宽延迟积与吞吐上限的算法传输层的性能上限是可以算出来的这一点很多人不知道导致优化时全凭感觉。核心公式只有一个吞吐上限 窗口大小 / RTT反过来要打满某条链路需要的窗口是窗口 带宽 × RTT这个乘积叫带宽延迟积BDP。举个具体例子。假设链路带宽 1Gbps往返时延 40ms。BDP 1Gbps × 0.04s 40Mbit 5MB。也就是说接收窗口至少要 5MB这条链路才可能跑满。而很多系统的默认接收缓冲区只有几百 KB实际能达到的吞吐就被死死压在几十 Mbps你怎么优化应用代码都没用。# 看当前连接的窗口和重传情况 ss -ti dst 目标IP # 相关内核参数 sysctl net.ipv4.tcp_rmem # 接收缓冲区 最小/默认/最大 sysctl net.ipv4.tcp_wmem # 发送缓冲区 sysctl net.core.rmem_max输出里的cwnd拥塞窗口和rtt是最关键的两个值用cwnd / rtt就能估算出这条连接当前的实时吞吐上限。如果发现cwnd一直上不去说明要么在丢包触发了拥塞控制降窗要么受限于接收方通告的窗口。提示调大缓冲区不是无脑越大越好。缓冲区过大会让缓冲区膨胀问题更严重——排队的数据变多延迟升高实时业务的体验反而变差。对延迟敏感的服务宁可窗口小一点、丢包少一点也不要为了吞吐把缓冲堆到几十 MB。3.3 重传、RTO 与拥塞控制的实际影响丢包之后的重传策略直接决定了应用的体感。TCP 有两套重传触发机制超时重传RTO和快速重传。RTO 是基于 RTT 估算出来的有最小值限制Linux 上一般是 200ms 起初始值 1 秒。快速重传靠的是重复 ACK收到三个重复 ACK 就立刻重传不用等 RTO 到期。这两者的差别在抓包里非常明显一次超时重传至少带来几百毫秒的停顿而快速重传通常只带来一个 RTT 的额外延迟。所以当你看到某个接口的 P99 延迟总是集中在几百毫秒这个档位第一反应就该去看有没有超时重传。# 统计重传情况 netstat -s | grep -i retrans ss -ti | grep -E retrans|rtoretrans计数持续增长说明链路在丢包rto值突然变大说明 RTT 估算被打乱了。如果重传率超过 1%任何应用层优化都是徒劳得先解决链路问题。拥塞控制算法这块现在主流的两个选择是 CUBIC 和 BBR。CUBIC 基于丢包判断拥塞在有随机丢包的链路上会过度降速BBR 基于带宽和 RTT 建模对随机丢包不敏感在高延迟长肥管道上优势明显。切换方式sysctl net.ipv4.tcp_congestion_control # 查看当前 sysctl -w net.ipv4.tcp_congestion_controlbbr但 BBR 不是万金油。它在有深缓冲的网络里可能挤占其他流的带宽在多租户环境下要慎重。我自己的经验是内网同机房用默认的 CUBIC 就够了跨地域长链路再考虑 BBR而且一定要做 A/B 对比别一上线就全量切。3.4 TIME_WAIT 与端口耗尽的定量分析这是传输层被误解最深的一块。先把结论摆出来TIME_WAIT是主动关闭方的正常状态作用是保证最后一个 ACK 能到达、让旧连接的残余报文在网络中消散防止它们污染新连接。它存在是有道理的不是系统的毛病。问题出在量上。假设一台机器作为客户端每条连接都是它主动关闭那么每条连接都会占用一个本地端口进入TIME_WAIT持续 60 秒Linux 的 2MSL 默认值。可用端口范围默认是 32768 到 60999大约 28000 个。那么理论上每秒能建立的新连接上限是28000 / 60 ≈ 466 次/秒超过这个量就会出现无法分配请求的地址报错。注意这个上限还受四元组限制——同一个目标 IP 加目标端口下能用的源端口就只有这些。应对思路有几条我按推荐程度排改成长连接。这是治本的办法尤其是内部服务之间用连接池复用别每次请求都新建连接。让服务端主动关闭。谁主动关闭谁承担 TIME_WAIT把这个负担转移到连接数更少的服务端通常更合理。扩大端口范围。net.ipv4.ip_local_port_range调大到 1024-65535能提升三四倍上限成本极低。开tcp_tw_reuse。注意这个参数只对出站连接有效而且需要开启时间戳选项配合本质是复用那些安全的 TIME_WAIT 端口。它对服务端的入站连接毫无帮助。绝对不要动tcp_tw_recycle。这个参数在新版本内核里已经被移除老版本上开启它会在 NAT 环境下造成大量连接失败属于典型的优化出故障。注意很多人把tcp_fin_timeout当成缩短 TIME_WAIT 的参数这是错的。它控制的是 FIN_WAIT_2 状态的持续时间跟 TIME_WAIT 没有关系。改它不但解决不了端口耗尽还可能让半关闭的连接更快被回收引发别的问题。3.5 UDP 与 QUIC什么时候该换车UDP 本身极简无连接、不保证顺序、不重传、不做拥塞控制头部只有 8 字节。它的价值就在于什么都不管把控制权完全交给应用层。DNS 查询、实时音视频、游戏状态同步、监控数据上报这些场景要么数据小到不值得建连要么丢了也无所谓要么应用自己有更好的补偿策略用 UDP 是合适的。真正值得单独说的是 QUIC。它跑在 UDP 之上但在用户态实现了可靠传输、拥塞控制、加密握手把 TCP 那套机制重新做了一遍还顺带解决了几个历史包袱建连只需 0 到 1 个 RTT、连接迁移不受 IP 变化影响、没有 TCP 层的队头阻塞。对于移动端和弱网场景收益是实打实的。但换协议的成本别低估。QUIC 走 UDP意味着中间设备对 UDP 的处理策略会直接影响它——有些环境对 UDP 限速甚至直接封禁有些负载均衡器不支持 UDP 转发。我的建议是先在灰度环境验证整条链路对 UDP 的友好度再决定要不要切同时保留 TCP 回退能力别把鸡蛋放一个篮子里。4. 应用层协议差异、选型与那些默认值陷阱4.1 DNS 解析链路与缓存层级DNS 是整个请求链路的第一步也是最容易被忽略的一步。一次完整的解析要经过浏览器缓存 → 操作系统缓存 → 本地 DNS 服务器 → 根服务器 → 顶级域服务器 → 权威服务器。每一层都有自己的 TTL实际生效的缓存时间取最短的那个。我梳理 DNS 时重点关注三件事。第一是解析耗时首次解析可能要几十到上百毫秒如果每次请求都触发解析累积起来很可观。第二是缓存行为很多运行时环境有自己的 DNS 缓存比如某些语言的虚拟机会缓存解析结果改了 DNS 记录之后不重启进程就是不生效这个坑我踩过不止一次。第三是多记录与故障转移一个域名配多条 A 记录时客户端拿到的是全部还是部分、失败后会不会换一条不同实现差别很大。dig trace example.internal # 从根开始追溯解析路径 dig 8.8.8.8 example.internal # 指定 DNS 服务器查询 dig short example.internal # 只看结果容器环境里还要多看一层/etc/resolv.conf里面的search域和ndots设置会让某些域名多解析好几次。默认ndots:5意味着域名里的点数少于 5 个时会先尝试拼接 search 域失败后再试绝对域名——一个简单的服务名可能触发四五次查询每次几十毫秒加起来就是明显的延迟。4.2 HTTP 三个版本的差异与选择HTTP/1.1 的核心问题是队头阻塞同一个连接上前一个请求没响应完后一个就得等着。浏览器靠开多个连接缓解但连接数有限且每个连接都要独立握手。HTTP/2 引入了多路复用一个连接上并行跑多个流还做了头部压缩HPACK和服务器推送推送后来基本被弃用了。但它的多路复用建立在 TCP 之上一旦底层丢包所有流都被阻塞这叫 TCP 层队头阻塞。丢包率高的链路上HTTP/2 的表现可能还不如 HTTP/1.1 开多个连接。HTTP/3 把传输层换成了 QUIC彻底绕开了这个问题还支持 0-RTT 恢复和连接迁移。代价是 UDP 链路的兼容性风险和一部分中间设备的支持度。维度HTTP/1.1HTTP/2HTTP/3传输层TCPTCPQUIC(UDP)多路复用无靠多连接有有队头阻塞应用层有TCP 层有基本消除头部压缩无HPACKQPACK建连开销1 RTT TLS1 RTT TLS0-1 RTT中间设备兼容最好好需验证我的实践结论是内部服务之间 HTTP/1.1 加连接池完全够用别为了先进去上 HTTP/2多路复用带来的复杂度在低延迟内网里收益有限。对外的移动端接口、跨地域调用HTTP/2 或 HTTP/3 的收益才明显。切换之前一定要确认负载均衡、网关、WAF 这些中间环节的支持情况。4.3 TLS 握手、证书链与常见配置错误TLS 握手是 HTTPS 请求里除 DNS 之外最容易被忽视的耗时点。TLS 1.2 完整握手需要两次往返TLS 1.3 压到一次会话复用时甚至可以做到 0-RTT。对于高频短连接握手成本占比很高所以会话复用session ticket 或 session ID必须开。# 查看握手过程和耗时 openssl s_client -connect example.internal:443 -servername example.internal -tls1_3 # 只看证书链和有效期 echo | openssl s_client -connect example.internal:443 2/dev/null | openssl x509 -noout -dates -subject常见的配置问题有这么几类。证书链不全是最高频的服务端只配了站点证书没配中间证书浏览器能自动补全所以看不出问题但很多客户端和 SDK 不会补直接握手失败。时间不同步导致证书校验失败的案例也不少机器时钟漂移几分钟就可能让有效期校验挂掉。SNI 未配置则会在多域名共用同一 IP 的架构下返回错误证书。提示验证证书链完整性最靠谱的办法是关掉本地根证书缓存去连或者用一个干净的客户端环境测。本地能通不代表线上能通这类问题在测试环境极难复现。4.4 长连接与推送协议怎么选需要服务端主动推数据时可选方案有 WebSocket、SSE、MQTT、gRPC 流这么几种选型不能只看哪个火要看场景的约束。方案底层方向适合场景主要限制WebSocketTCPHTTP 升级双向交互式应用、协同编辑需处理断线重连、心跳SSEHTTP服务端到客户端通知、日志流、进度推送单向HTTP/1.1 下占连接MQTTTCP双向发布订阅物联网、弱网设备需要 Broker协议栈较重gRPC 流HTTP/2双向服务间流式调用依赖 HTTP/2浏览器支持有限选了长连接就绕不开三个问题心跳间隔怎么定、断线怎么重连、连接数怎么控制。心跳太频繁浪费资源太稀疏则发现不了中间设备的空闲超时。我的经验值是心跳间隔要比链路上最短的空闲超时小一截比如负载均衡 60 秒断空闲连接心跳就设 30 秒留出一倍余量。重连要做指数退避不然服务端重启的瞬间会被所有客户端的重连请求打垮。5. 抓包与排障实操把梳理落到现场5.1 工具分工与常用参数工具不在多在于知道什么时候用哪个。下面这张表是我日常的主力配置工具主要用途典型命令ss看本机连接状态、窗口、队列ss -lntp、ss -ti dst x.x.x.xtcpdump抓包适合服务器端tcpdump -i eth0 -nn port 80 -w a.pcapWireshark分析包看时序和重传图形化配合 IO 图看吞吐digDNS 解析排查dig trace、dig shortcurl端到端耗时分析curl -w分段计时mtr逐跳丢包与延迟mtr -rwzbc 100 目标openssl s_clientTLS 握手细节见上文curl的分段计时特别值得单独讲因为它能一次性把 DNS、TCP、TLS、首字节、总耗时都拆开定位速度快得惊人curl -o /dev/null -s -w dns:%{time_namelookup} tcp:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n https://example.internal/health各项之间做差就能知道瓶颈在哪tcp - dns是 TCP 握手耗时tls - tcp是 TLS 握手耗时ttfb - tls是服务端处理耗时total - ttfb是传输耗时。哪一段异常就往哪一层深挖。5.2 一次大包必超时的完整排障记录说个我印象最深的案例整个过程能体现梳理的价值。现象某项目的文件上传接口小文件几十 KB100% 成功超过 200KB 的文件 100% 超时报错是读取超时不是连接失败。服务端日志里能看到请求进来但没有完整接收记录。第一轮假设应用层的读取超时设得太短。检查配置读取超时 30 秒200KB 的文件正常几百毫秒就该传完排除。第二轮假设服务端接收缓冲区太小。查了tcp_rmem默认值和最大值都不算小而且连接建立后窗口能涨上去排除。第三轮抓包。在服务端抓包看到一个非常清晰的特征——客户端发到 1448 字节的包之后后续的包服务端一个都没收到客户端在不停重传同一个序列号重传间隔从 200ms 一路涨到几秒最后客户端放弃。这个特征指向两个可能丢包或者路径 MTU 不匹配。接着做验证在客户端往服务端发带 DF 标志的大包发现 1472 字节不通1400 字节通1300 字节通。到这里基本确定了——链路上有一段 MTU 小于 1500大包被丢弃而 ICMP 的需要分片消息没有回传客户端不知道要减小 MSS陷入死循环。根因客户端和服务端之间加了一层隧道封装隧道头占用了额外字节实际 MTU 降到 1400 左右。但客户端和服务端的网卡 MTU 还是 1500双方协商出来的 MSS 是 1460超出实际路径能力。解决在网关的出方向做 MSS 钳制把 SYN 包里的 MSS 选项改写成 1360给隧道留足余量。改完之后大文件上传立刻恢复正常重传计数归零。沉淀这件事之后我把路径 MTU 探测加进了上线前检查清单凡是跨网段、跨云、加了隧道封装的链路一律先测一遍实际 MTU。5.3 常见问题速查表下面这些是我这些年反复遇到的按现象整理配上判断依据和处理方向能覆盖大部分日常场景现象判断依据处理方向大量 CLOSE_WAITss -s里该状态计数持续增长查应用代码确认是否漏了 close端口耗尽报错ss -s显示 TIME_WAIT 占满端口范围上连接池、扩端口范围、改主动关闭方连接建立但无响应全连接队列溢出计数增长调大 backlog优化慢处理逻辑偶发几百毫秒延迟ss -ti里 retrans 增长查丢包看是不是拥塞控制过度降速首字节慢、传输快curl -w里 ttfb 占比大查服务端处理、DNS、TLS 握手解析偶发失败dig多次查询结果不一致查多记录、TTL、本地缓存长连接被莫名断开空闲超时时间与心跳间隔对比心跳间隔缩到超时的一半证书校验失败openssl s_client报链不完整补中间证书、校时同网段不通ip neigh出现 INCOMPLETE查二层隔离、ARP 限速、VLAN 配置隧道后吞吐骤降BDP 计算 vs 实际窗口调缓冲区、确认窗口缩放已开启5.4 我自己的梳理清单最后把我每次接手新系统都会过一遍的清单放出来都是踩过坑之后加进去的顺序就是排查时的优先级确认实际 MTU跨网段、有隧道的链路必测记下安全值写进配置。确认 DNS 解析路径和 TTL含容器内resolv.conf的ndots和search。确认连接建立方式长连接还是短连接连接池大小有没有 keepalive。确认各层超时时间客户端、负载均衡、网关、应用取最短的那个作为实际约束。确认窗口和缓冲区按 BDP 算一遍看默认值够不够。确认拥塞控制算法跨地域长链路重点看必要时做 A/B。确认 TLS 版本与证书链用干净客户端验证别信本地。确认中间设备对 ICMP 和 UDP 的策略影响 PMTUD 和 QUIC 可用性。确认重传率和丢包率基线有个基线异常时才能对比出来。把上面九项写进文档不写下来三个月后自己都忘了。我个人在实际操作中的体会是网络协议梳理最大的价值不在于懂多少协议而在于遇到问题时知道先看哪一层、用什么命令验证、看到什么现象对应什么原因。协议细节可以现查但这套从现象到层级到命令的映射关系只能靠自己一遍遍排障积累。上面那些表和清单就是我这些年积累下来的东西你可以直接拿去改成自己系统的版本比从头摸索要省不少时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑