news 2026/9/15 18:05:36

网络与IO故障排查实战:从TCP重传到连接超时

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
网络与IO故障排查实战:从TCP重传到连接超时

排查网络与IO问题,是每个后端开发者和运维都绕不开的硬仗。我做了十多年一线运维和架构,回头去看,线上那些最磨人的故障,十有七八都出在这两条路上——网络在悄悄丢包,或者磁盘、连接在某个瞬间堵住了。更麻烦的是,这两类问题经常缠在一起出现:应用层看到的是超时,抓包看到的是重传,系统指标里又是IO等待偏高,三者要对齐才知道问题到底卡在哪。

这一章,我把自己这些年处理网络与IO异常的经验,整理成一套可以直接照着用的排查方法。从最底层的原理讲起,逐步展开网络侧、IO侧的具体排查命令和案例,最后用一次完整的排障复盘把整套思路串起来。不管你是刚入行一两年的后端,还是已经被线上故障磨了许久的运维,这套方法都能直接搬过去用。我会尽量少说空洞的理论,多讲“你在终端里按下回车之后能看到什么、下一步该敲什么”这类实操细节。

1. 网络与IO为什么必须放在一起排查

1.1 网络传输的本质就是IO

很多人习惯把“网络问题”和“IO问题”当成两个独立领域,这是排障时最容易走弯路的地方。其实在网络通信协议栈里,socket就是一个文件描述符,你读写网络数据,本质上就是在做IO读写。只不过普通文件的数据写在磁盘扇区上,而socket的数据通过网卡发到对端,路径不同,底层的系统调用、内核缓冲、阻塞与非阻塞机制都是同一套。

理解了这一点,你就明白为什么一个Java服务在报WebSocket连接断开的时候,日志里会出现failed to send websocket request: io这样的错误——这个io不是指磁盘,而是指socket IO操作失败了。排查时如果不先建立这个认知,很容易被“明明是网络问题为什么会报IO错误”给绕进去。

我经常用一个很简单的类比来解释这种关系:网络和IO就像城市道路系统和物流仓库的关系,包裹(数据)既要在马路上跑(网络传输),又要在仓库里分拣暂存(IO缓冲)。马路堵了会影响包裹进出仓库,仓库叉车坏了也会让马路上排起长队。排障时不把两者放在一起看,你往往只能看到表面现象,抓不到真正的堵点。

1.2 一条数据在“网络+IO”链路里经历了什么

以一次最普通的HTTP接口调用为例,数据从客户端到服务端再回来,通常要经过这些环节:网卡接收中断触发驱动收包,数据从网卡DMA到内核缓冲区,协议栈做TCP/IP解析,然后数据被复制到socket接收队列,最后通过read系统调用进入应用程序的用户态缓冲区。服务端处理完后,响应数据再反向走一遍:用户态缓冲区 -> socket发送队列 -> 内核协议栈 -> 网卡DMA -> 物理链路。

任何一个环节出问题,表现到应用层可能就是同一个词——超时。网卡线缆老化会导致物理层丢包;协议栈缓冲区太小会导致TCP吞吐上不去;socket队列满了会导致read或write阻塞;应用进程内GC停顿会导致数据在用户态缓冲区里滞留。这就是为什么我排查问题从不先看代码逻辑,而是先沿着这条链路一层层排除。

1.3 我的三层定位法:先定性、再分层、后定位

这套方法听起来简单,但很多人执行不到位,一上来就抓包或改代码,最后查了半天发现是虚惊一场。我自己总结了三个步骤:先定性——判断问题到底是网络链路、IO性能还是应用逻辑导致的,这一步通常用几个系统指标和简单的连通性命令就能确定大方向;再分层——网络和IO各自都有很多层,比如网络有物理层、链路层、网络层、传输层、应用层,IO有用户态、内核态、设备层,要确定问题出在哪一层;后定位——缩小到具体进程、具体文件、具体连接,再用抓包或strace这类工具拿到铁证。

这套顺序在后面几节会反复用到。你只要记住一个原则:能用系统自带命令在两分钟内确认的事,就不要急着上抓包工具;能缩小到一个连接一个文件,就不要停留在“某某服务好像不太稳定”这种模糊结论上。

2. 网络问题排查实战:从网卡到应用协议逐层过

2.1 先分清现象:不通、卡顿、还是时断时续

网络问题的现象基本上就三类:完全不通、稳定卡顿、间歇性异常。我用一个简单方法来区分:先ping服务器的网关或内网地址,再ping公网地址,再telnet某个业务端口。

如果网关都ping不通,问题大概率在物理链路或网卡配置;如果内网通、公网卡,考虑路由和DNS;如果ping通但业务不通,问题在端口监听或防火墙;如果ping和业务都通,只是偶发超时,那就要重点排查TCP重传、连接复用和中间设备空闲超时。这三个方向用的命令、看的数据完全不一样,方向选错就会浪费大量时间。我自己就犯过好几次“一上来就抓包,结果抓了一小时发现问题根本不在这一层”的错误。

2.2 链路质量与带宽:ping、mtr、ethtool、iperf3的组合

排查有线网络卡顿或链路质量问题时,我习惯按这个顺序敲命令。

ping -i 0.2 目标IP,连续发几百个包,看丢包率和延迟抖动。如果只是延迟偶尔漂移,先记下来,不用太紧张。接着用mtr 目标IP看每一跳的延迟和丢包率,它可以快速定位是本地、运营商骨干还是对端的问题。注意有些节点丢包是ICMP限速导致的假象,需要多跑几次确认,不要一看某个节点丢包就觉得是那里坏了。

ethtool 网卡名同样值得一看。千兆网卡协商成百兆导致网速慢,是我见过频率最高的“低级”问题之一,网线老化、水晶头接触不良都可能导致协商降级。最后是iperf3 -s在服务端、iperf3 -c在客户端,用流量实测TCP/UDP带宽。这一步能排除“应用层看着慢,其实链路没问题”的情况。很多在线测速工具只是测浏览器下载速度,测出来慢不一定对,内网两台机器之间的问题,iperf3才是真正靠谱的手段。

2.3 端口与连接状态:ss、telnet、nc到底该怎么用

确认了链路没问题,下一步看端口和服务。ss -lntp可以列出所有监听端口和对应进程,我的经验是先确认端口真的在监听,再确认监听地址是0.0.0.0还是127.0.0.1——后者意味着只有本机自己能访问,外部自然连不上。

telnet和nc的区别在于:telnet适合快速看端口通不通,而nc(netcat)还可以用来做UDP测试、发送原始数据、做最简单的端口转发。UDP服务的排查用不了telnet,就得靠nc -uz 目标IP 端口这种写法。还有一点经常被忽略:不要只看“端口通”就觉得服务正常。TCP连接建立成功只能说明三次握手完成,应用层的健康状态需要用curl或业务自带的心跳接口去确认。

2.4 抓包与协议分析:从tcpdump到wireshark

如果端口、链路都正常,但业务依然异常,那就该上抓包工具了。tcpdump是我的首选,轻量、在服务端直接跑。最常用的两种写法:tcpdump -i eth0 host 目标IP and port 8080 -w /tmp/xxx.pcap,抓完用wireshark打开分析;如果只需要看握手和断开过程,tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0'可以过滤掉绝大部分数据包。

抓包最重要的不是看数据内容,而是看三类信号:TCP重传(Retransmission)、乱序(Out-of-Order)、零窗口(Zero Window)。重传多说明链路丢包或对端处理不过来,零窗口说明接收方缓冲区已满、应用层没及时读走数据。这两类信号正好对应网络和IO两个方向,拿到它们,问题归因就有了方向。

2.5 容易被忽略的配置类问题:从Ubuntu的netplan说起

有一类问题纯粹是配置错误导致的,现象千奇百怪。最典型的有三种:Ubuntu 18.04之后用netplan管理网络,改完网卡IP只执行service networking restart是没用的,必须netplan generatenetplan apply;DNS解析异常导致域名访问卡顿,但直接ping IP又是通的;Windows局域网里“网络发现已关闭”导致共享文件不可见,需要打开网络发现并启用网络和共享中心里的相关选项。

排查这类问题,我的建议是先看配置再看日志。ip addr看地址,ip route看路由,resolvectl status看DNS,netplan get看当前生效配置。很多“默认配置”第一次看可能觉得没问题,但一旦和实际网络拓扑不符,就会变成那种“看起来一切正常但就是不通”的悬案。

3. IO问题排查实战:找到谁在拖慢系统

3.1 先搞懂几个IO指标:吞吐、IOPS、延迟、利用率

我遇到过不少同事,一看到iostat打出来的%util是99%,就喊着“磁盘满了”,其实%util高不一定代表磁盘慢。机械硬盘在处理随机IO时,%util接近100%确实意味着饱和;但NVMe固态硬盘有很高的并行度,%util即使到100%,可能还有大量排队能力没有用完,这时更应该看实际延迟和吞吐量是否达到预期。

真正要关注的是这几个指标的组合:r/sw/s代表每秒读写次数(IOPS),rkB/swkB/s代表吞吐量,await代表IO请求平均处理时间,%util代表设备繁忙程度。它们要配合着看,比如IOPS不高但await很高,可能是单个大块写入阻塞;IOPS很高且await也在涨,就是明显的磁盘压力。

3.2 三步定位IO瓶颈:iostat、pidstat、strace

第一次发现系统的IO性能明显下降时,别急着想着换硬件,按顺序做三步定位。

第一步,iostat -x 1 3,整体看是哪个设备有问题,确定是磁盘、网络还是其他块设备。第二步,pidstat -d 1定位到具体进程,看每个进程的读写IO情况。第三步,如果还定位不到,用strace -p 进程号 -e trace=read,write,openat,fsync 2>&1 | head -100,看进程在做哪些系统调用,判断是否有频繁的write或fsync在刷盘。

我有个习惯,查IO问题一定会顺手跑一下vmstat 1,直接看两个参数:wa(CPU等待IO的时间占比)和cs(上下文切换次数)。如果wa一直在20%以上,基本能确认IO是系统卡顿的元凶。

3.3 真实案例:日志刷盘把整个服务拖垮

之前处理过一个Java服务,运行一段时间后开始偶发超时,io指标很高。用pidstat定位后发现是一个日志框架线程在疯狂刷盘,原因是异步日志模式下,队列容量太小,产生日志的速度太快,背压导致刷盘线程长时间满负荷运行。加上磁盘本身是机械盘,随机写性能有限,整个IO路径就被拖住了。

解决办法并不复杂:把日志框架的异步队列调大,调整刷盘策略,对非关键日志降级,同时把日志和业务数据放到不同磁盘。改完之后,IO利用率立刻降下来,接口耗时也恢复正常。这个案例的启发性在于:应用层代码写得再快,如果底层IO路径规划不合理,一切都是白搭。

3.4 别忘了嵌入式与硬件场景的IO问题

网络与IO问题排查不止出现在服务器上。STM32开发里,PF0和PF1默认复用为JTAG功能,想当普通IO用必须先禁用JTAG,否则怎么初始化都是高阻态;FPGA的IO设计里,推挽、开漏、上拉的区别直接影响外部信号是否有效——开漏输出要外接上拉电阻才能输出高电平,很多人在这上面栽过跟头。

工业相机设备也常遇到IO触发问题。以海康相机IO拍照为例,接线必须区分PNP和NPN两种线型,接反了光耦不导通,软件里再怎么配置也不会触发拍照。我建议这类问题先拿万用表量一下IO电平,而不是反复改SDK参数。另外做PLC程序联调时,Factory IO这类虚拟仿真软件能省不少事,但仿真里IO映射和真实硬件的地址对应关系一定要核对清楚,地址映射错了,仿真跑得再顺,上现场一样乱。

4. 网络与IO交织的典型故障:连接异常断开

4.1 这类报错到底在说什么

很多开发都见过这种报错:stream disconnected before completion: failed to send websocket request: io error,或者stream disconnected before completion: io error: peer closed connection with。这种报错经常让人一头雾水,但其实拆开看就很简单。

peer closed connection with——对端主动关闭了连接。发送FIN包是正常关闭,发送RST包是异常关闭,两者含义完全不同,抓包一眼就能区分。failed to send websocket request——客户端在发送WebSocket请求时发现连接已经不可用。也就是说“客户端以为连接还活着,实际早就被对端或中间设备断掉了”。

这类故障的典型场景是:客户端维护了一个连接池或长连接复用机制,服务端或中间的负载均衡设备在空闲一段时间后主动断开了连接,而客户端没有及时感知,下次请求复用连接时才发现对方已经关了很久了。

4.2 用时间轴法定位连接断开问题

遇到这类问题,我最核心的排查手段是“时间轴对齐”:把应用日志、系统连接状态、抓包文件三者的时间戳放在一起对比。

具体操作是:先确认异常发生的时间点,在应用日志里找到对应的报错记录;再用ss -tnp | grep 端口查看当前连接状态,看有没有大量TIME_WAITCLOSE_WAIT;最后用tcpdump抓包,看断开时是收到FIN还是RST,以及最后一次数据交互到断开的间隔是多少。

有一个坑我要重点提示:负载均衡和云网关的空闲超时时间通常不会写在文档里,默认可能只有30到60秒。如果你的应用心跳间隔设置成90秒,那连接肯定会被中间设备掐断。这种情况抓包时甚至会看不到任何异常,因为FIN包是中间设备发的,服务端和客户端两边看起来都“很干净”。

4.3 从系统参数到应用层的修复思路

排查清楚之后,修复方向一般有三个。第一,在系统层调整TCP keepalive参数,缩短保活探测时间,比如把net.ipv4.tcp_keepalive_time从默认的7200秒调小,但这只能让本机更早发现连接失效,不能阻止中间设备断开。第二,在应用层加心跳机制,WebSocket协议本身就支持ping/pong帧,普通TCP连接则可以让客户端周期性发送轻量心跳数据,间隔要小于中间设备的空闲超时时间。第三,在客户端做连接失效快速重连,发请求前先确认连接可用,发现异常立即重建连接。

我个人更推荐应用层心跳加自动重连的组合,因为系统层的keepalive在很多云环境里不生效,中间设备不一定会转发保活探测包。

5. 一次完整排障复盘:接口偶发超时的根因

5.1 现场情况

有一次值班,线上一个核心接口的P99延迟从平时的50毫秒飙到800毫秒,并且伴随偶发超时。监控面板上,CPU、内存、带宽都没有明显异常,磁盘使用率也只是正常水平,但从监控图表能观察到IO util有周期性尖峰。

我先按照三层定位法做了初步判断:ping内网网关和外网地址都正常,ss查看业务端口还在监听,curl调用本地接口正常。于是我把问题方向初步定在网络链路和IO路径之间的某个环节,而不是业务代码逻辑。

5.2 逐层深入的过程

先跑vmstat 1,wa基本在0附近,排除磁盘IO阻塞。再跑ss -tnp统计连接数,发现TIME_WAIT连接数量很多,高峰期接近几十万。大量TIME_WAIT本身不是坏事,但结合客户端IP分布,我怀疑是连接复用策略有问题。

然后上tcpdump抓了五分钟包,很快看到大量TCP Retransmission,而且重传的报文都集中在几个和外部API服务交互的连接上,这些连接都使用了长连接池。进一步分析发现,这些连接的空闲时间超过了外部API服务所在网关的空闲超时阈值,连接被中间层静默关闭,客户端却还在复用旧连接。

5.3 根因是什么

问题其实是两个因素的叠加:外部API网关的空闲超时时间只有60秒,而我们的客户端心跳间隔设置成了120秒,超过超时时间的连接全部被网关断开;加上连接池里的连接没有及时清理和重建,新请求不断命中失效连接,触发TCP重传和重连风暴。重传本身消耗了额外网络带宽,同时很多线程在等待重连过程中阻塞,表现为接口延迟飙升。

这个根因里既有网络问题(连接被中间设备断开、重传),也有IO问题(线程阻塞、连接池等待),单独看任何一类数据都只能看到一半真相。

5.4 修复与验证

修复方案主要改了三处:把心跳间隔从120秒调整到45秒,确保比网关超时短;连接池增加空闲连接检测,失效连接直接剔除;客户端增加请求失败后的自动重连和重试机制,但重试需要加随机退避,防止同时重连打垮网关。

改动灰度上线后,TCP重传数量立刻下降,P99延迟在观察周期内稳定回到50毫秒以下。这次排障给我最大的感受是,连接类问题往往不是单一技术栈的问题,跨系统联调时要格外关注两边的超时和心跳参数是否匹配。

6. 一份随手可查的排障工具箱

6.1 网络排查常用命令速查

整理了一张表,适合贴在手边,出问题时直接对应查询:

排查目标命令说明
本机IP与网卡ip addr查看IP、掩码、MAC、网卡状态
路由与网关ip route确认默认网关,排查路由异常
连通性与延迟ping -i 0.2 目标IP连续ping,看丢包率和延迟抖动
链路每跳质量mtr 目标IP类似traceroute,显示每跳丢包
网卡速率ethtool 网卡名看协商速率,排查百兆降级
带宽实测iperf3 -s/iperf3 -c内网带宽测试,比测速网站可靠
监听端口ss -lntp查看端口和对应进程
连接状态统计ss -s看TCP状态分布
DNS解析dig 域名/nslookup 域名排查DNS解析导致的卡顿
抓包tcpdump -i eth0 host IP and port 端口 -w file.pcap抓包后wireshark分析

6.2 IO排查常用命令速查

IO侧的命令同样整理成表:

排查目标命令说明
整体IO负载iostat -x 1看设备util、await、IOPS、吞吐
系统负载与IO等待vmstat 1wa字段高说明进程在等IO
进程级IOpidstat -d 1看具体进程的读写速率
实时IO排行iotop交互式查看哪个进程IO最高
打开的文件与连接lsof -p PID看进程打开的fd和socket
系统调用追踪strace -p PID -e trace=read,write,fsync定位程序卡在哪个调用上
磁盘调度器cat /sys/block/sda/queue/scheduler查看IO调度器,确认是否需要调整

6.3 画拓扑图和记录排障过程

很多人忽略了一个非常实用的习惯:给系统画一张网络拓扑图。不需要多专业,用draw.io这类工具,把客户端、负载均衡、业务服务器、数据库、外部依赖画清楚,线上出问题时对着拓扑图走一遍链路,能省下大量时间。我在排障时会随手把时间轴、命令、输出截图、结论记录到一份文档里,这些记录就是以后遇到类似问题时的最宝贵参考。

另外,网络调试助手这类工具用来做TCP和UDP通信测试非常顺手,适合验证本机和服务端之间的收发能力。做批量部署时,同传软件在网络规划和分区规划合理的情况下能大幅提升效率,但前提仍然是先把网络排查思路理顺,否则同传过程中的卡顿和丢包也会被误判为软件问题。

最后分享一点个人体会。干这行这么久,我觉得排障最考验人的不是命令记得多熟,而是心态和方法。遇到网络与IO问题,先别急着怀疑“是不是代码写错了”或者“是不是要换机器”,深吸一口气,沿着链路一层层查下去,每一步都用数据说话,最终一定能找到那个真正的堵点。很多时候我们觉得某个问题诡异,其实只是没有把网络和IO这两条线放到一起看而已。希望这一章的实战内容,能让你下一次面对这类问题的时候,心里更有底。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 18:05:33

频率约束下桁架结构减重的NSM-LSHADE-CnEpSin算法与Matlab实践

简介:一份基于Matlab的NSM LSHADE CnEpSin优化算法源码,面向研究差分进化算法改进或频率约束桁架结构优化的工程师与科研人员,解决LSHADE-CnEpSin在带约束工程优化中收敛精度与效率不足的问题。压缩包共18个文件,全部为.m脚本&…

作者头像 李华
网站建设 2026/9/15 18:04:57

Oracle 19c RAC报INS-06006排查:Linux 8.3下SSH互信与scp替换实战

先说结论:在Linux 8.3上装Oracle 19c RAC,Grid Infrastructure跑到ssh互信检查时卡住报INS-06006,这个报错本身不一定是OpenSSH的锅,但确实是OpenSSH版本越新越容易踩的坑。我那次是在客户内网环境,不能随便打OS补丁、…

作者头像 李华
网站建设 2026/9/15 18:03:45

VeraCrypt 磁盘加密完整指南:新手编译与数字签名避坑教程

VeraCrypt 磁盘加密完整指南:新手编译与数字签名避坑教程 【免费下载链接】VeraCrypt Disk encryption with strong security based on TrueCrypt 项目地址: https://gitcode.com/GitHub_Trending/ve/VeraCrypt VeraCrypt 是一款开源磁盘加密软件&#xff0c…

作者头像 李华
网站建设 2026/9/15 18:03:34

HFSS螺旋线圈优化设计与高效仿真实践指南

做线圈设计绕不开HFSS,尤其当你要对着一个螺旋线圈做优化设计时,建模和高效仿真这两关能卡住不少人。我刚带完一个无线充电线圈的项目,从模型搭建到参数优化再到后处理,把整个流程重新捋了一遍,发现很多问题其实都是共…

作者头像 李华
网站建设 2026/9/15 18:03:09

瑞利信道仿真MATLAB源码程序详解:基于2021a的操作录像与参数调优

简介:面向通信工程、电子信息类专业的学生与课程设计使用者,这套MATLAB源码程序基于matlab2021a,用于瑞利信道仿真与多径衰落建模,可帮助理解信道参数设置及仿真流程。包内共3个文件,包括2个m源码文件和1个avi操作录像…

作者头像 李华