丢包常见原因概览:物理层到应用层

排查丢包问题,最忌讳的就是一上来就抓包、看TCP重传。我干了这么多年网络,踩过最大的坑就是「凭感觉猜原因」。有一次,客户说业务丢包严重,我直接上Wireshark分析应用层,折腾两天没结果,最后发现是机房的网线被老鼠咬了一口。所以,你得有条理——从物理层往应用层一层层撸。

这一章,我把常见的丢包原因按OSI模型分层列出来。你排查时顺着这个清单扫一遍,基本能锁定问题。

丢包常见原因概览:物理层→应用层 物理层 • 光纤/电缆物理损坏、衰减过大 • 光模块/电口故障、信号干扰(电磁/串扰) 数据链路层 • CRC/FCS错误、帧校验失败 • 端口缓冲区溢出、半双工冲突、MAC地址欺骗/泛洪 网络层 • 路由环路、黑洞路由、MTU不匹配(分片失败) • 策略路由/ACL/QoS丢弃、ICMP不可达/超时 传输层 • TCP连接队列溢出(backlog满)、重传超时 • 端口耗尽、NAT端口映射冲突、TIME_WAIT堆积 应用层 • 应用处理慢→读缓冲区阻塞、socket buffer过小 • 协议设计缺陷(HTTP队头阻塞)、FD泄露、消息超时

物理层:线缆与信号问题

物理层丢包是最容易被忽略的,因为大家总觉得「做网络的都是搞IP的,谁会去看光功率」。我一开始也这样,后来被干过两次才长记性。

  • 光纤衰减:光模块实际功率低于接收灵敏度。查一下show interface transceiver,Rx/Tx功率在阈值附近就开始丢包了。
  • 电缆/网线:水电工的网线打得不好,或者用了劣质跳线。我一个项目里,连续三天间歇性丢包,最后发现是配线架的RJ45压线只压进去4根。
  • 电磁干扰:铜缆靠近大功率电机、变频器,48V电源线没走强电槽。你想想看,网络设备旁边放个电焊机,那丢包基本没法查。
  • 光模块/电口故障:模块自身温度过高、金手指氧化、插接不紧。我习惯先交换模块测试,比抓包快得多。
️ 经验提示: 查物理层丢包,先看设备日志里的CRC align errors和input errors。如果伴随FCS错帧,90%是物理层问题。

数据链路层:帧结构与缓冲区

过了物理层,数据链路层是第二道坎。交换机的MAC地址表、端口缓冲区、生成树协议都可能引起丢包。

  • CRC/FCS错误:物理层信号畸变导致帧校验失败。这类丢包在二层统计里能看到runts/giants。
  • 端口缓冲区溢出:流量超过端口转发能力,或遇到微突发。比如千兆口突然收到2Gbps,没有流控的话直接丢。
  • 半双工/全双工不匹配:两端收发冲突。老式交换机容易出这问题。我记得有一次,华为和思科对接,自动协商没成功,硬生生掉了一半的包。
  • MAC泛洪/环路:二层环路没跑STP,或者ARP攻击把交换机MAC表撑爆了。

网络层:路由与IP策略

网络层丢包的原因,说白了就是「包找不到路」或者「路不让走」。

  • 路由环路/黑洞:静态路由配错、邻居震荡、路由收敛慢。我遇到过一个七跳的环路,TTL耗尽后ICMP超时返回,业务就断了。
  • MTU不匹配:路径上某个链路的MTU小于包大小,又不支持分片。特别是EFS/Jumbo Frame配置不一致时,大包直接丢弃。
  • QoS/ACL策略:限速、丢优先级、ACL deny。客户经常抱怨「怎么某几个IP访问不了」,查到最后是安全策略误配置。
  • ICMP不可达/过滤:防火墙禁用了ICMP,但路径MTU需要ICMP通知,结果包死在中间——这叫PMTUD黑洞。
⚠ 避坑指南: 我曾经排查一个跨国丢包,抓了三天流量,最后发现是VPN隧道出口的MSS被固定死了。检查路径MTU时,不要只看路由,要把所有隧道、GRE、IPsec都算进去。

传输层:连接与队列

传输层丢包往往是TCP协议本身的行为,或者连接资源耗尽。

  • TCP连接队列溢出:listen()的backlog不够,或者应用程序accept太慢。你会看到TCP SYN cookies启用,甚至直接丢SYN包。
  • 重传超时:对端没收到ACK,或链路延迟太大,RTO计算超长。TCP的重传看似是传输层问题,根源可能在底层丢包,但这里说的是上层主动引起的丢包。
  • 端口耗尽/连接数限制:客户端拼命建连接,TIME_WAIT堆积导致端口不够。Linux的net.ipv4.ip_local_port_range默认32768-60999,用完就报错。
  • NAT/防火墙状态表溢出:并发连接超过设备规格,新连接直接DROP。

嗯,这里要注意:很多运维看到TCP重传就去查网络,其实应该是先查服务器端netstat -s里的segments retransmitted,再结合应用日志。

应用层:业务逻辑与速率失配

应用层丢包是最容易被误解的,因为用户只看到「请求没响应」,但网络层面包其实是正常转发的。

  • 读取缓慢导致Socket buffer满:应用代码是单线程,处理不过来,发送方的缓冲区写不进去,内核直接丢包。数据包到网卡就被扔了,甚至应用层没收到。
  • HTTP队头阻塞:HTTP/1.1管道化时,前一个请求卡住,后续全队阻塞。这个是应用协议特性,不是网络问题,但用户感知和丢包一样。
  • 文件描述符泄露:程序长期不关闭socket,fd耗光,新连接无法建立。系统日志经常出现Too many open files。
  • 消息超时/无应答:自定义协议里,客户端重发消息导致服务端堆积,最终丢消息。这种问题常常被误报为「网络丢包」。

一句话总结: 丢包的原因,物理层最隐蔽但最容易验证,应用层最明显但最难定位。排查时一定要从物理层开始,一层层排除,不要直接扑到应用抓包上。

各层丢包特征速查表

下面这个表我一直在用,排查时对照一下症状就知道该先查哪一层。

层次 典型症状 排查方向
物理层 CRC错帧、输入错误持续增长、光模块告警 查线缆、光功率、模块温度
数据链路层 MAC振荡、端口丢包计数器飙升、STP状态变化 查环路、端口协商、缓冲区
网络层 ICMP超时、路由跳数异常、QoS丢弃 查路由表、MTU、ACL
传输层 TCP重传率高、SYN超时、连接建立失败 查backlog、端口范围、NAT表
应用层 应用报错无网络层异常、连接断开但抓包正常 查代码处理速度、buffer大小、fd数量

我个人习惯是在每个排查环节先做show interface counters errors,如果没有二层错误,再往上查。这样能过滤掉至少一半的假阳性。