
处理过几十次线上故障之后你会发现大多数高难度问题最后都落在两个词上网络IO。它们一个负责让数据在设备之间流动一个负责让数据在设备内部进出表面分工明确可真到故障现场这两者往往缠得死死的。你这边刚怀疑是网络抖动那边数据又卡在磁盘IO上不动你以为改改socket参数就完事结果根因居然是日志落盘太慢。这一章我想把网络与IO问题排查的实战方法摊开来写从最常见的现象、最常用的命令到那些只有踩过坑才知道的细节一次性讲清楚。不管你是后端开发、运维工程师还是在搞嵌入式、单片机的硬件同学只要你需要和数据进出打交道这份排查思路应该都能帮上忙。1. 网络与IO问题排查的整体框架1.1 网络与IO为什么总被放在一起很多人一开始不理解网络和IO明明是两个方向为什么要放在一章里讲。但如果你从操作系统往下看就会发现它们的边界远比想象中模糊。简单说一个socket的读写本质就是文件IO底层都在操作文件描述符。数据包到达网卡后经过硬中断、软中断、协议栈处理最后放进socket缓冲区这条完整路径本身就是一条IO链路。早期Java面试必考的BIO、NIO、AIO讨论的核心对象也是网络IO。所以你在应用层看到的“网络卡了”很大程度上是底层IO路径上的某个环节堵了。反过来的情况也天天发生。一次数据同步没跑完你怀疑磁盘在拖后腿结果抓包一看是网卡在疯狂重传一次WebSocket连接莫名其妙断开你盯着应用代码查了半天最后发现是中间设备的空闲超时把它掐了。这就是我把它们放在一起的原因网络问题和IO问题经常互为表象拆开查往往两边都找不到真凶。这一章的定位很明确不追求协议栈的百科全书而是从实战角度梳理排查路径。适合刚接触在线问题处理的后端工程师和运维同学也适合写过嵌入式代码、但想补一补系统级网络视角的硬件开发者。1.2 排查前必做的四件事我见过太多人在问题还没搞清楚的时候就急着动手“修”重启服务、换IP、调超时参数……结果问题隔天又冒出来。现在我不管遇到多紧急的故障都会先花几分钟回答四个问题问题从什么时间开始是持续出现还是间歇出现如果间歇大概是什么周期影响范围多大是单台机器、某个网段、某类客户端还是全部业务故障前后有没有变更部署、升级、配置改动、网络调整哪怕只是有人拔插了一根网线都算。现象能不能复现有没有一个最小操作可以稳定触发这四个问题的答案通常能直接排除掉一半的猜测。我还会顺手把故障前后30分钟的应用日志、系统日志、监控数据全部留档。监控数据缺失的时候至少保存一份dmesg、journalctl和ss -s的输出。真实排查里很多结论都是靠“事发前后几分钟的现场”逆推出来的。留档越早定位越准。这条经验几乎适用于所有线上故障但网络与IO问题尤其依赖现场数据因为很多网络异常转瞬即逝过了那几分钟再想复现比登天还难。1.3 一套可以复制的分层排查流程排查网络与IO问题说到底是一条“分层验证”的路。第一层是给现象分类连不上、时断时续、速度慢、IO报错这四类的排查入口完全不同别混在一起碰运气。第二层按协议栈分层验证。从客户端到服务端依次看应用层日志、传输层连接状态、网络层路由与连通性、数据链路层是否丢包。对应的工具就是ping、telnet/nc、ss、tcpdump、mtr这一套。每一层有每一层的证据别跳过中间的层直接下结论。第三层是IO侧继续往下拆。应用层看是否阻塞、是否频繁上下文切换系统层看文件描述符、缓冲区、磁盘队列深度硬件层看中断、驱动、物理链路。我自己的习惯是“两头看”客户端和服务端同时采集数据再在中间找分界线。比如客户端telnet不通服务端ss -lnt显示端口监听正常那问题基本落在路由、防火墙或中间设备上两边都正常但业务还是超时那就要回到应用和IO路径里找。这套流程不是教科书理论而是我处理线上问题时一直在用的肌肉记忆。照着这个顺序走至少能保证不遗漏、不盲目重启。2. 网络层问题实战排查与工具落地2.1 连接建不起来三次握手现场还原连接建不起来是最高频也最容易误判的一类问题。表面看是“网络不通”但把三次握手拆开每一步都有各自的原因。第一步客户端发出SYN服务端没有回SYNACK。这种情况大概率是防火墙、安全组把端口或IP拦了。云服务器上十有八九是安全组规则没放行自建机房则优先检查iptables。第二步服务端回了SYNACK但客户端收不到。这时候要在客户端抓包确认重点怀疑服务端把服务监听在了127.0.0.1上对公网或局域网不可见或者中间路由把回包丢了。第三步SYN反复重传服务端netstat出现大量SYN_RECV状态这说明半连接队列backlog已经满了。出现这种情况要么是服务端并发握手请求量巨大要么是应用层处理accept速度跟不上。我自己的快速定位套路是这四条命令ss -lnt看端口是否监听监听地址是0.0.0.0还是127.0.0.1nc -zv 目标IP 端口测试端口连通性tcpdump -i 网卡 tcp port 端口直接看握手的包有没有到、有没有回。有一回排查一台服务“端口不通”ss显示监听在[::]客户端却用IPv4访问结果发现系统IPv6的dual-stack映射没有正常启用数据包一直没被接受。折腾半个多小时最后把监听配置改成*才解决。所以监听地址的细节非常关键判断时要同时确认*、0.0.0.0和[::]三种情况的差异。2.2 长连接频繁断开与“stream disconnected”类报错的追踪实时系统大量使用WebSocket和自研长连接最恼人的报错之一长这样stream disconnected before completion: failed to send websocket request: io error: peer closed connection with这段报错拆开看就是三件事第一连接没有完成预期的数据交换就断了第二出错的时机在“发送WebSocket请求”之前也就是连接建立后没能撑到发送第三底层IO层报告是“对端关闭了连接”对方主动发了FIN或RST。遇到这种问题第一步不是改代码而是抓包看谁先断的。用tcpdump -i eth0 tcp port 8080 -w ws.pcap保存报文再用Wireshark打开过滤FIN和RST标志。常见结果分两种对端先发FIN正常的连接关闭流程最常见原因是空闲超时。很多代理层、负载均衡器默认60秒或90秒断开空闲连接而客户端心跳间隔比这个还长连接自然被回收。对端直接发RST非正常关闭。常见原因是对端应用崩溃后操作系统强制回收socket或者安全设备检测到异常后重置。我处理过的最典型故障是客户端心跳90秒一次中间有一台网关设备的空闲连接超时设置是60秒结果每天固定时段批量断连。最后把心跳改成30秒客户端再加上指数退避重连问题才彻底消失。另一个值得提的现象是桌面应用在切换Wi-Fi后频繁提示“等待网络”。这种问题往往不是链路故障而是网络栈没有及时感知网卡切换旧的地址配置还占着。Windows下重置Winsock目录或重启网络适配器通常能解决macOS下则需要确认多出口路由的优先级。这些都是长连接类问题的变体根子都在“连接生命周期管理不当”。这里有一条重要的排查原则长连接问题不要只看应用日志一定要把中间设备的超时参数、负载均衡的 idle timeout、操作系统net.ipv4.tcp_keepalive_time全部拉出来对比。很多所谓的神秘断连其实就是某个你不记得的中间配置在“定时清理”。2.3 网络测速与带宽延迟的真实含义用户报“网速慢”的时候别急着下结论。先搞清楚慢在“带宽”还是“延迟”。这俩经常被混为一谈。带宽是管道的粗细决定一次能传多少数据延迟是管道的长度决定一个数据包来回要多久。一个形象的类比是水管越粗能灌更多的水但水从一头流到另一头的时间还是由管道长度决定。在线网页测速工具测得快只能说明你的互联网管道够粗不等于到目标服务器的连接质量就好。我在排查跨地域访问慢的问题时几乎不看网页测速结果而是用iperf3本地实测。在两台机器上分别装好一端运行iperf3 -s另一端运行iperf3 -c 目标IP -t 60然后看带宽、重传率和抖动三个指标。最容易被忽略的是重传率。带宽数字可能很好看但重传率超过1%就说明链路上有丢包用户依然会觉得卡。测速还要注意场景测吞吐用多线程iperf3 -P 4测单用户的真实体验则要关注单线程延迟和丢包率用ping -c 100就能拿到基础数据无线环境要先排除本机Wi-Fi信号干扰再谈运营商链路质量。另外弱网环境下的“慢”和故障性的“慢”要区分开。前者是物理限制后者是配置或设备问题。比如卫星链路天然延迟高跨境专线天然需要较大的TCP缓冲区才能跑满带宽这些不是故障调整预期和TCP调优参数才是正确方向。2.4 实测记录两台电脑UDP通信调试TCP复杂但相对好排查UDP看着简单坑反而更多。我帮人调过好几回“UDP发不出/收不到”的问题基本都是忽略了UDP的两个特性无连接、不可靠。有一次需要两台电脑用UDP联调Windows上开网络调试助手一端做发送一端做接收。发送端选了UDP模式填了接收端的IP和端口点发送接收端却什么都没收到。排查步骤是这样的第一接收端要先进入“绑定本地端口”的状态。UDP不像TCP有显式握手接收端必须提前把自己的UDP socket绑定到目标端口上。调试助手如果没点启动或连接端口没绑上数据自然来一个丢一个。第二检查Windows防火墙。调试助手首次运行时如果弹窗被误点“取消”UDP包会在入站方向被拦截。临时测试可以直接加一条入站允许规则生产环境则要按最小权限原则审慎配置。第三关注MTU。UDP大包超过路径MTU时会分片分片丢失会导致整个数据报被丢弃。测试时先发小包确认通路再逐步加大包体超过1472字节就要考虑分片问题。最终那次问题就是防火墙拦截放行后一切正常。UDP调试还有一个经验是在接收端用Wireshark或tcpdump抓包先确认数据有没有到网卡。到了网卡但应用收不到是应用层问题根本没到网卡是网络链路问题。这一条同样适用所有网络问题是我在定位“对端没反应”时百试不爽的思路。3. 从硬件到软件IO问题的分类排查3.1 嵌入式GPIO推挽、开漏、上下拉与驱动能力IO问题不只有磁盘和Socket嵌入式设备上的GPIO也算而且更容易让新手翻车。做STM32、FPGA、ESP8266这类项目时最常踩的坑集中在引脚工作模式和驱动能力上。先说推挽输出和开漏输出的区别。推挽输出既能输出高电平也能输出低电平驱动能力强开漏输出只能主动拉低输出高电平要靠外部上拉电阻。这就带来一个常见现象开漏输出的引脚接LED或继电器如果忘了加上拉电阻输出高电平其实是浮空的用万用表量电压会忽高忽低外部设备动作不可靠。再看IO驱动能力。STM32的GPIO灌电流和拉电流一般在几毫安到二十毫安之间具体看型号和配置。驱动LED没问题但直接驱动继电器、蜂鸣器这类电感性和大电流负载就会出现电压跌落、引脚发热甚至烧毁。正确做法是通过三极管或MOS管做信号放大或者用ULN2003这类驱动芯片。ESP8266这类芯片IO资源有限需要扩展IO口时要特别留意时序和上下拉配置。用I2C或SPI扩展IOI2C总线上拉电阻的阻值、SCL频率与线长的匹配都会影响稳定性。我在实际项目里遇到过I2C设备偶发无响应排查到最后发现是上拉电阻用了10k总线电容偏大导致上升沿太慢换成4.7k后恢复正常。工业场景里的IO映射问题也值得一提。比如汇川H5U这类PLC的高速计数器IO硬件组态以及CC-Link模块的IO地址映射组态错误会导致读写地址错位。排查时要按“模块硬件拨码 → 组态配置 → 地址映射表 → 程序引用”的顺序逐一核对不能只盯着程序看。FPGA里的IO同样不只是“高或低”那么简单Bank电压、LVCMOS/LVDS电平标准、片内上下拉、ODT都对应不同的硬件设计配置错了轻则信号不可靠重则芯片发热。这些细节在Factory IO这类仿真软件里是看不出来的仿真通过之后必须回到真实硬件上测量验证。3.2 存储与文件IO性能骤降怎么查“IO性能明显下降了”几乎是我收到过的最模糊的报障描述。模糊不要紧怕的是没有数据支撑。我的处理套路是先用iostat -x 1盯几分钟重点看%util、await、svctm三个指标。%util接近100%说明磁盘一直处于忙碌状态await高说明IO请求在队列里等待时间很长svctm一般代表硬件处理单个IO的时间。如果await远大于svctm队列积压已经很明显了。但光看这三个还不够要结合场景才能定位根因。有一次客户说数据库写入变慢%util接近90%磁盘本身也没什么坏道。后来用iotop一看是另一个日志进程在疯狂刷盘把IO带宽占满。停掉它之后数据库写入马上恢复。所以在IO排查里先找“谁在用IO”往往比“IO为什么慢”更重要。iotop、pidstat -d都能定位到具体进程。另外别忽略“软性”原因磁盘剩余空间太少块分配会变慢inode耗尽df -i一看就是100%文件系统日志策略在高并发写场景下会放大写入量SSD在过热或寿命耗尽后降速现象非常明显用smartctl可以查温度、磨损和备用块。还有一类特别的IO问题来自代码层面频繁fsync。每次fsync都要等数据真正落盘如果业务代码里每个请求都做一次性能必然被拖垮。这类问题改代码往往比换硬件更有效因为瓶颈不在设备而在软件对落盘时机的过度敏感。3.3 Java IO 与 NIO 的选型和典型坑后端同学一听到“网络与IO”大概率会想到Java里的BIO、NIO。很多年过去了这个话题依然值得重新理解一遍因为面试会问出了问题更要会答。传统BIOBlocking IO的模型是“一个连接一个线程”连接来了就分配线程读不到数据就阻塞在read()。连接数少没问题连接数一上来线程数跟着涨线程上下文切换和内存占用就会拖垮系统。NIO的核心变化是“一个线程管很多连接”用Selector监听多个Channel的事件有事件才处理没有就阻塞在select()上减少无效等待。这也是Netty高性能的底层基础。但NIO不是银弹。我见过几个项目换成NIO后反而更频繁出问题典型坑有三个忘了处理Selector空转。某些情况下select()会被立即唤醒但没有任何事件如果不控制空转CPU会一直跑满日志里表现为CPU占用异常高但连接数不多缓冲区大小设置不当。ByteBuffer太小会导致频繁半包处理太大又浪费内存要根据实际报文大小反复压测调整没有正确处理read()返回-1的情况。在NIO里read()返回-1代表对端已经关闭连接正确做法是关闭Channel并清理资源而不是继续往业务层抛数据。选型建议其实很简单连接数少、单条数据量大用BIO也够连接数多、请求模型是短小频发直接上NIO或基于Netty的框架。不要为了技术时髦去换模型先算清并发连接数和线程开销再说。4. 网络与IO交织问题的联动分析4.1 网络超时背后的IO等待链条有一类问题特别有意思客户端报“网络超时”抓包看服务端也确实回了响应但就是慢。这种问题经常不是网络的锅而是服务端应用在处理请求时等待了某个IO操作。举个例子一个查询接口突然从50ms变成2s。抓包发现TCP握手很快服务端接收到请求之后第一个响应字节等了1.8s。这时候就要往服务端内部看数据库查询慢、文件读取慢、还是线程池排队用strace -p 进程PID跟一下系统调用或者用链路追踪工具看耗时分布很快就能定位到“卡在哪个调用上”。排查这类问题的核心是“划清时间边界”网络耗时、应用处理耗时、IO等待耗时各占多少。没有这个拆分你会一直以为自己修的是网络问题实际上改了几轮参数都白费。我个人的习惯是在服务端抓包时看TCP流的时间戳计算客户端发出请求到服务端ACK之间的时间以及服务端处理完后到第一个响应包的时间。简单粗算至少能判断慢发生在“进服务端之前”“出服务端之后”还是“服务端自己卡住了”。这个时间边界的划分是网络与IO交织问题里最重要的一个动作。4.2 连接状态异常CLOSE_WAIT、TIME_WAIT与句柄泄漏TCP断开过程中的异常状态是网络与IO交织问题的重灾区。先看TIME_WAIT。主动关闭连接的一方在收到对端ACK后会进入TIME_WAIT状态等待2MSLLinux下通常60秒之后才释放。高并发的短连接服务TIME_WAIT堆积到一定程度会占用大量本地端口新连接就可能建立失败。解决办法是开启连接复用、适当调整net.ipv4.tcp_tw_reuse注意不要用tcp_tw_recycle它在NAT环境下会引发严重问题。再看CLOSE_WAIT。这个状态堆积原因几乎只有一个对端关闭了连接但本端应用没有调用close()释放socket。每一个CLOSE_WAIT都意味着一个文件描述符被占着堆积到进程的文件描述符上限后新连接会报too many open files。排查CLOSE_WAIT用ss -tan state close-wait查看卡住的连接再用lsof -p 进程PID | wc -l看句柄总数。更关键的是找代码里哪个连接池没有正确释放。我看到过好几起线上事故表面是“连接数过多”根因是某个开源客户端库在异常分支没有释放连接。这类问题修代码往往比调内核参数更治本。这一节真正想强调的是Socket也好、文件也好在操作系统层面都是文件描述符也就是IO资源。CLOSE_WAIT和句柄泄漏本质是IO资源生命周期管理失败。排查网络连接问题一定要带着“这后面也是IO”的视角去看否则永远只看到表象。4.3 结合网络拓扑与中间设备做最终判断很多问题单看两端是看不出来的必须把网络拓扑拉出来。比如用户说“访问某服务慢”但在公司内网ping得通那就需要画一遍完整链路用户终端 → 接入交换机 → 核心交换 → 防火墙 → 负载均衡 → 应用服务器 → 数据库。排查时用tracerouteWindows上是tracert看每一跳的延迟用mtr持续探测看丢包集中在哪一跳。如果丢包发生在跨网段的核心链路那问题就指向网络设备和线路而不是你的服务器。中间设备里防火墙、负载均衡、上网行为管理设备是最容易“背锅”也最容易被忽略的它们默认的超时、限速、连接数限制策略会直接裁剪长连接、限制带宽。NAT设备的会话表如果满了新连接也会建立失败。这里顺便提一下南向网络和北向网络的区别。在云网运维里南向通常指控制面与底层设备之间的接口北向指对上提供的API或服务接口。区分这两个方向实际排查时能帮你快速判断问题出在“配置下发”还是“业务承接”不至于一直在错误方向上打转。带着拓扑图去排查最大的好处是能切换视角从单台机器的视角跳出来站到整条链路的视角看问题。很多疑难问题比如间歇性丢包、长连接被莫名重置只有站在链路中间才能看到真相。5. 经典实战案例与常见问题速查5.1 案例一收银终端“网络发现已关闭”的完整处理有一回商场收银系统报障Windows收银机上弹出一行提示“网络发现已关闭。网络计算机和设备不可见。请启用网络和共享中……”。最初商家以为是网络故障网线换了、交换机端口也换了问题依旧。这个提示的本质是Windows的网络发现功能被关闭或者被防火墙策略拦截。排查顺序是这样的第一确认网络配置文件。打开“设置 → 网络和Internet → 查看网络属性”看当前网络是“专用网络”还是“公用网络”。Windows对公用网络默认关闭网络发现这是安全策略不是故障。第二打开“控制面板 → 网络和共享中心 → 更改高级共享设置”在“专用”配置下启用网络发现顺手把“文件和打印机共享”也打开。第三启动相关服务。网络发现依赖四个服务Function Discovery Provider Host、Function Discovery Resource Publication、SSDP Discovery、UPnP Device Host。用services.msc把这几个服务设置为自动启动并启动。第四检查防火墙。入站规则里是否允许“网络发现”和“文件和打印机共享”如果之前手动配置过安全策略可能把对应项禁用了。这个案例的关键教训是主机的“网络问题”不一定是链路问题可能是系统自身策略把功能关了。排查网络问题时要习惯先问一句是链路不通还是设备根本不想通。5.2 案例二WebSocket网关反复断开的一次定位过程这个案例的根因很简单心跳间隔比中间设备的空闲超时长。现象是业务端日志频繁出现stream disconnected before completion: failed to send websocket request: io error: peer closed connection with每隔一段时间就批量断连客户端自动重连之后又好一会儿。排查过程分四步走用tcpdump在网关两侧抓包确认FIN包来自负载均衡设备而不是客户端或服务端登录负载均衡查看连接超时配置发现空闲连接超时是60秒客户端心跳间隔是90秒超过60秒没有数据交互时LB主动断开连接修改客户端心跳为30秒同时在代码里实现断线重连和指数退避。这次之后我养成了一个习惯任何长连接系统上线前都要确认一条“连接生命周期”清单。客户端心跳间隔、服务端空闲超时、中间设备空闲超时、防火墙会话超时这四个参数必须满足心跳间隔小于所有空闲超时的最小值。这是长连接稳定性的第一原则。类似的“连不上”问题还要考虑Linux内核参数。net.ipv4.tcp_keepalive_time默认7200秒对很多业务来说太长了要主动探测死链可以适当调小但也要评估探测流量对网络设备的压力。5.3 常见问题速查表把排查经历沉淀成表是我自己最常用的一种记忆方式。这里整理了一张精简版基本覆盖网络与IO领域的高频症状现象优先怀疑快速验证常见解法连接超时防火墙/路由/对端未监听ping、nc -zv、ss -lnt放行安全组/修复监听地址连接被重置RST应用崩溃/安全设备拦截抓包看RST来源修应用异常退出/调整安全策略大量SYN_RECV半连接队列满/扫描攻击ss -tan统计增大backlog/排查攻击源大量TIME_WAIT主动断开方短连接过多ss -tan state time-wait | wc -l开启连接复用/优化连接池大量CLOSE_WAIT应用未释放连接lsof -p PID修代码正确close网络时延大链路拥塞/中间设备mtr看每跳优化路由/联系线路方带宽占用高大流量业务/异常外传iftop、nethogs限速/QoS/定位异常进程磁盘Util高磁盘繁忙/进程争抢iostat -x、iotop定位进程/扩容/优化刷盘磁盘await高队列积压/硬件降速iostat -x、smartctl隔离进程/更换硬件GPIO输出不稳定上下拉/驱动能力不足万用表测电平/看波形加上拉/加驱动芯片这张表不能解决所有问题但它能保证你在故障面前不至于脑子空白。先按表里最快的验证命令试一遍大多数问题已经能缩小到很明确的范围内。剩下查不清的基本都是需要抓包、看源码、做时序分析的深水区问题那时候再上Wireshark和strace也不迟。5.4 我常用的网络与IO排查工具箱工具不在多关键是要顺手。我自己的工具清单分成三组。第一组是网络连通类ping、traceroute/tracert、mtr。这组只解决“通不通”“经过哪里”的问题是故障初判的第一站。第二组是连接与协议类telnet/nc用来测端口ss看连接状态tcpdump和Wireshark做抓包分析。tcpdump建议大家都熟练掌握几个必用参数-i指定网卡、-w写文件、-nn不解析域名和端口名。抓包文件交给Wireshark做图形化分析效率会高很多。第三组是性能与IO类top/htop看负载iostat -x看磁盘iotop看进程pidstat -d看每个进程的IOstrace跟踪系统调用lsof查文件描述符。网络带宽侧还有iftop和nethogs前者看网卡整体流量后者定位是哪个进程在占用流量。一体化工具我也用比如有些人习惯的“网络运维工具箱”v8.x系列把网卡状态、端口检测、路由跟踪、DNS查询等常用功能打包到一个界面里对批量巡检和快速初判确实有帮助。但我的建议是底层命令行工具必须自己会用一体化工具只是提效不能在关键时刻变成黑盒。最后还有一个压箱底的习惯所有命令的输出都养成保存到文件的习惯。ss -s /tmp/net_state_时间戳.txt、iostat -x 1 10 /tmp/io_state_时间戳.txt。故障复盘时这些原始数据比截图可靠得多也方便回溯当天网络和IO的状态曲线。写到这里我把这些年踩过的网络与IO的坑挑最典型的整理了一遍。说到底这类问题靠的不是某个神秘工具而是一套“分层拆解、两头验证”的思维网络慢就抓包看谁在等IO慢就看谁在抢两者分不清时用时间戳把耗时切成段。我在实际排查中最深的体会是不要急着下“网络问题”或“IO问题”的结论先花几分钟确认边界再动手。大部分疑难杂症真正定位到根因时往往发现是配置参数、资源生命周期这些小细节在作怪。希望这些案例和命令能让你下次遇到类似问题时少走一点弯路更快找到那个隐藏在层层日志背后的真相。