网络与IO问题排查实战:从现象到根因的完整指南
写网络和IO问题排查这块我一直觉得是整个运维和开发工作里最考验综合功底的环节。很多时候业务出问题表面上是网络不通实际上可能是IO卡死或者服务端处理不过来反过来也成立。遇到这种场景要是不分清楚问题的边界上来就一顿抓瞎很容易排查一整天毫无进展。所以我更愿意把这一章当作一套实战手册来讲而不是堆概念。适合谁看呢后端开发、运维工程师、嵌入式或硬件调试的同学甚至自己电脑联网出点小毛病想弄明白原因的普通用户都能从中找到可操作的思路。1. 先想清楚到底是网络问题还是IO问题1.1 两类问题的本质区别网络问题指的是数据在网络上传输时出了问题比如连接建立不了、延迟高、丢包、对端突然断连等等。IO问题则更偏向本机或设备内部的处理能力包括磁盘读写、内存换页、网卡收发包、文件系统等待等。两者最大的区别在于网络问题是“两点之间”的问题IO问题是“单点内部”的问题。但实际排查过程中两者经常会交织在一起。比如某个服务查询很慢你第一反应是网络慢结果排查了一通发现是磁盘IO接近瓶颈所有请求都卡在等数据库返回数据而数据库的慢是因为磁盘在读日志时排队。反过来网络出现问题也可能诱发IO异常比如大量无效重传会让网卡中断频繁导致CPU软中断占用过高。所以动手之前先确认问题究竟属于哪一类能省掉大量无用功。1.2 从现象倒推原因的通用思路我个人的习惯是分三步走先复现或收集现象。包括报错信息、持续时间、影响范围、是否偶发等。缩小范围。从客户端到服务端逐段确认网络通不通、端口通不通、协议是否正确、应用层是否有响应。抓关键数据。用抓包工具或监控命令抓取当时的真实流量和系统状态结合日志定位根因。不要一上来就怀疑代码、怀疑硬件、怀疑内核参数。先看数据先看清楚现象再去推导原因。很多时候排查浪费的时间都是因为没搞清楚现象就开始猜了。2. 工具箱这些命令和工具到底在解决什么问题2.1 网络层基础命令从ping到tcpdump网络排查常用的命令其实就那几类关键是要知道每个命令适合什么场景ping最基础的连通性测试测试本机到目标IP的链路是否可达同时初步判断延迟和丢包率。要注意的是ping通不代表应用就通很多服务对ICMP包做了忽略所以不要看到ping通就认为网络没问题。traceroute / mtr查看数据包经过的路径和每一跳的延迟变化。mtr比traceroute更适合排查因为它会持续检测每一跳的丢包率能更直观地判断丢包发生在哪一段。telnet / nc测试TCP端口是否可连接。telnet到目标IP指定端口如果端口通会显示连接到对方如果不通会超时或直接拒绝。ncnetcat功能更强可以测试UDP端口也可以用来做简单的端口转发。curl / wget测试HTTP/HTTPS接口可以指定请求头、POST数据、查看详细响应时间。curl的-w参数能输出连接时间、TLS握手时间、首字节时间等信息对定位HTTP层问题非常有帮助。tcpdump / Wireshark抓包分析看数据包的传输过程包括三次握手、四次挥手、重传、RST等。Wireshark适合在本地分析pcap文件tcpdump适合在服务器上抓包。2.2 应用层与协议层工具除了网络层很多问题是出在应用层协议上的比如HTTP请求超时、WebSocket连接中断、数据库连接池被占满等。这时候需要看应用日志同时用抓包工具确认TCP或协议层面的交互。ss / netstat查看系统当前的网络连接状态确认连接是ESTABLISHED还是TIME_WAIT端口是否被监听。ss比netstat快很多推荐优先使用。lsof查看某个端口由哪个进程占用或者某个进程打开了哪些文件描述符和网络连接。strace跟踪进程的系统调用能看到进程在读取网络数据、磁盘文件时的具体行为和耗时是定位应用层和IO层问题的利器。perf系统级性能分析工具可以分析CPU周期、缓存命中率、锁竞争等。虽然它不是专门的网络排查工具但在定位软中断过高、锁等待导致IO毛刺时非常有用。2.3 IO排查工具与关键指标IO问题的排查相对复杂一点因为IO不光是磁盘读写还包括网络IO、内存换页、设备IO等。但大部分情况下我们遇到的IO问题都是磁盘IO或文件系统层面的。iostat查看磁盘的平均服务时间、等待时间、利用率。重点看%util、await、svctm这几个指标。%util超过80%就要警惕磁盘接近饱和但也不能完全依赖这个数值因为RAID或SSD的IO合并能力不同。iotop实时查看每个进程的磁盘读写速率和IO等待时间非常适合定位“哪个进程在疯狂读写磁盘”这类问题。df / du / lsof排查磁盘空间不足、文件被删除但仍占用空间、以及打开文件数过多的情况。dmesg查看内核日志能发现磁盘错误、文件系统错误、网络设备异常等。2.4 别忘了系统基础状态在排查网络和IO问题时系统的基础状态往往能提供重要线索。sar系统活动报告能看到CPU、内存、IO、网络的历史数据。如果在问题发生时没有实时抓取数据sar的历史记录就是事后分析的关键。vmstat查看进程、内存、换页、IO的总体情况。如果wa过高说明系统大量时间在等待磁盘IO。/procLinux的虚拟文件系统里面包含几乎所有内核运行时数据。比如/proc/net/dev可以看每块网卡的流量统计/proc/diskstats可以看磁盘IO统计。3. 网络问题排查几个高频场景的完整过程3.1 连接建立失败先判断是网络不通还是端口不通这个场景太经典了以致于很多人一想到网络问题就上来反复ping。但如我前面所说ping通不代表端口通。实际操作中排查连接建立失败要按这个顺序来先确认目标IP是否可达。在客户端直接ping目标IP如果完全丢包可能是网络线路、防火墙、目标主机宕机等问题。再确认目标端口是否有服务在监听。在目标主机上执行ss -lnt确认端口状态如果端口没监听就要去看服务进程是否启动、配置的监听地址是否正确。如果端口在监听但客户端连不上就在客户端用telnet测试。telnet不通时用tcpdump同时抓客户端和服务端的网卡看SYN包有没有发出、有没有收到SYN-ACK。SYN没发出是本机防火墙或路由问题SYN发出但没收到响应说明包没到对端或被对端防火墙丢掉收到SYN-ACK但客户端不回ACK很可能是客户端本身的状态问题。有一年我处理过一个跨地域的服务连不上的问题现象是客户端偶发超时服务端CPU和网络流量都不高。抓包后发现客户端发出了SYN但对端迟迟不回SYN-ACK过一会儿直接返回RST。最后定位到是中间防火墙因为连接数限制丢包导致长时间没响应后自动发RST。这个问题如果不抓包光靠看CPU和连接数根本发现不了。3.2 连接中途断开的真相从WebSocket报错说起在热词里有个很典型的报错stream disconnected before completion: failed to send websocket request: io。这种报错的字面意思是在发送WebSocket请求时连接在完成之前就断开了。很多人看到这个报错会以为是代码里的异常处理逻辑有问题但其实它背后藏着多种可能对端主动关闭了连接。可能是服务端因为心跳超时、协议解析出错、空闲连接回收等原因主动断开。一般可以在服务端日志里看到“client closed connection”或“EOF”之类的记录。中间代理或网关主动断开。比如Nginx的proxy_read_timeout设置太短客户端还没传输完代理就认为超时了然后主动断开TCP连接客户端就会看到类似“peer closed connection”的错误。防火墙或负载均衡器因为空闲时间过长断开了连接。很多云环境的网络安全策略会清理长时间无流量的TCP连接但客户端并不知道还在等待数据等到再发包时对端已经关掉了。客户端自身IO阻塞导致发送超时。如果客户端的写缓冲满了、上行带宽拥堵或者CPU被其他任务抢占也会导致发送超时表现为IO错误。排查这个问题的思路是客户端抓包看发送流程服务端抓包看接收和断开的过程定位是哪个方向先发的FIN或RST。如果能看到先发FIN再对应查找服务端日志中关闭连接的原因如果看到的是RST就要看是否有防火墙或系统参数在干预。有次我遇到一个WebSocket每隔几分钟就自动断开抓包显示的确实是服务端先发了FIN而服务端日志里没有异常。最后发现是云平台的SLB设置了空闲连接超时5分钟没有消息就默认断开需要开启长连接保活或在应用层做心跳。3.3 延迟高与丢包用mtr逐跳定位网络延迟高的排查要区分是物理距离导致的固有延迟还是路径上的设备故障或拥塞导致的附加延迟。用ping测试只能得到最终结果很难知道延迟出在哪一跳。这时候用mtr就能看到每一跳的情况。mtr的输出格式里每一行代表经过的一个路由节点可以看到该节点的丢包率和平均延迟。如果某个节点丢包率很高而同一条路径上它后面的节点反而正常那通常不是这个节点本身丢包而是这个节点的ICMP限制策略不一定是故障。但如果丢包持续存在并且后面所有节点都跟着丢包那这个节点很可能就是瓶颈所在。另外用mtr排查时要注意运营商的路由设备优先级一般较低即使对ping包限速也不一定代表用户流量会丢包。所以判断要谨慎不能一看到某个节点丢包就觉得是它的问题要看它之后的节点表现。还有一个经验如果只是延迟高但没有丢包可能是链路拥塞、带宽跑满或者MTU设置不合理导致的分片与重组开销增大。可以尝试调整MTU或检查带宽使用率。3.4 传输速度上不去结合网络测速与TCP参数网络测速大家都不陌生无论是网页端还是App端测速原理基本一致建立多个TCP连接分别下载数据统计一段时间内的下载量计算传输速度。如果你在自建服务或服务器上遇到传输速度上不去除了考虑带宽限制还需要关注TCP层的因素。TCP传输速度跟拥塞窗口、接收缓冲、发送缓冲、延迟息息相关。最典型的问题是“带宽时延积”概念在长肥网络中如果接收窗口太小传输速率会被限制在窗口大小除以RTT的数值附近。举个例子RTT是100ms接收窗口默认是64KB那么最大吞吐大约就是64KB/100ms ≈ 6.4Mbps。这显然达不到百兆甚至千兆带宽。解决办法是调整系统的TCP窗口参数比如Linux下将net.core.rmem_max、net.core.wmem_max调大并且让TCP自动窗口缩放生效。另一个常见问题是网卡队列和中断绑定。如果网卡多队列没有开启或者中断全部集中在一个CPU核上高流量时会触发软中断瓶颈表现为CPU使用率不高但网络吞吐上不去。可以用ethtool看网卡队列配置开启多队列并把中断irqbalance打开缓解单核压力。3.5 网络协议分析别小看抓包这一步抓包看起来简单实际上有不少门道。首次抓包前建议先确定几个问题抓哪个网卡、抓多长时间、是否足够大的缓冲区、需要过滤哪些流量。我的习惯是先抓主机的所有流量用临时文件保存等复现后停止抓包再用Wireshark过滤分析这样最稳妥避免漏掉关键包。分析时先关注TCP三次握手、TLS握手、应用层协议交互这几个阶段。如果握手本身就花了很长时间那问题大概率在网络层或对端处理能力如果握手很快但应用层响应慢那问题在应用逻辑或数据库、第三方接口调用上。抓包还可以看到重传、乱序、窗口缩小等现象这些是网络质量问题的重要信号。4. IO问题排查深入到每一个等待4.1 磁盘IO瓶颈从iostat看懂读写的真实情况磁盘IO问题最常见的表现是服务响应变慢CPU的wa指标升高。但CPU高wa并不一定就是磁盘坏了也可能是文件系统、RAID重组、或某个进程在刷大量日志。iostat -x 1的输出里有几个指标要重点看%util设备忙碌的百分比。传统机械盘接近100%说明确实忙不过来但SSD因为有内部缓冲和高并发能力%util高也不一定代表性能就到顶要结合await判断。awaitIO请求在队列中等待和服务时间的平均值。如果await很高说明IO请求排队时间很长大概率是磁盘或后端存储池满载。svctm或新版本里的r_await、w_await设备实际处理一个请求的平均时间。如果svctm很低但await很高说明不是磁盘本身慢而是排队太严重。有一个场景我记得很清楚某服务每天晚上定时做全量备份磁盘IO直接在iostat里看到%util接近100%application日志的写入也跟着变慢进而导致大批请求超时。这时候的解决思路不一定是要换更快的磁盘而是考虑错峰备份、限流io、或者把备份目录放到另一个磁盘避免和业务IO争抢。4.2 jbd2 IO过高底层文件系统日志进程的干扰热词里有一个“jbd2 io过高”这个是Linux下ext4文件系统的日志守护进程负责维护文件系统的日志journal。正常运行中jbd2的IO占比较低如果它异常升高通常意味着文件系统层有大量元数据更新操作比如大量的小文件创建删除、目录操作、日志模式的配置不当等。有一个实际的案例服务器运行docker容器每天的日志文件很多都在小文件之间频繁创建导致jbd2持续写入journalIO负载长期偏高。后来调整了文件系统挂载参数将dataordered改成datawriteback减少日志同步次数问题缓解了不少。但要注意这个调整牺牲了一定的崩溃一致性建议只在能接受该风险的场景使用并且在修改前一定要备份和评估。另外如果你遇到jbd2写IO异常高还要检查是否频繁出现文件系统错误或磁盘坏道因为jbd2会在写入数据时触发多次重试导致IO异常升高。dmesg或journalctl里一般能看到端倪。4.3 应用层IO阻塞用strace看清每次系统调用磁盘和文件系统层面的问题排查完了如果发现系统层面IO压力不大但业务还是慢那问题可能出在应用自身。这时候strace是最直接的武器。strace -cp 可以汇总进程的系统调用时间和次数看到哪个系统调用耗时最长。如果想跟踪某个具体的文件操作可以strace -e tracefile -p 。有一次压缩一个超大目录性能很差strace发现进程几乎都在调用getdents遍历目录项也就是说文件数量巨大导致遍历本身就耗时巨大而不是压缩算法慢。后来做了目录结构优化减少单层目录的文件数问题就明显改善了。网络IO阻塞也一样。用strace跟踪一个HTTP服务的读写如果发现read或write调用经常是阻塞很久才返回说明对端或网络有延迟也可能是socket的send缓冲满了需要检测网络连接状态和流量情况。strace本身有开销生产环境使用要谨慎有条件的话先在测试环境复现。4.4 网络IO与异步模型从BIO到NIO再到io_uring网络相关的IO问题很多时候也牵涉到编程模型。传统的BIO阻塞IO模式下每个连接分配一个线程大量连接时线程上下文切换成了瓶颈。后来又有了NIO非阻塞IO配合多路复用器select、poll、epoll获得更高的并发能力。Java生态里的Netty、Java NIO都是基于这套模型。排查这类问题时如果发现服务线程数量很多、CPU上下文切换频繁可以考虑线程模型是不是有问题。可以用perf或者pidstat查看上下文切换次数如果cswch/s特别高说明线程调度频繁很可能是阻塞IO模型扛不住高并发。在Linux 5.1之后出现的io_uring把IO性能和异步处理又往前推了一大步。它用共享内存的机制规避了系统调用的开销特别适合高IOPS场景。在实际项目里如果遇到传统epollread/write模型的性能瓶颈可以尝试用io_uring等新异步模型做优化。不过io_uring的使用门槛较高对内核版本也有要求建议先从代价较小的参数调优和代码逻辑优化做起。4.5 硬件IO与嵌入式从高速计数器到FPGA口模式网络与IO问题不只在服务器和软件层嵌入式开发中也有大量IO排查需求。热词里的“h5u 高速计数器”还有“FPGA的IO有没有类似ARM的模式”都属于这个范畴。PLC领域里的高速计数器HSC用于处理高频脉冲信号如果IO输入频率超出硬件能力就会出现计数丢步。排查时除了确认输入信号的电压阈值和频率是否在规格范围内还要关注布线长度和干扰高频信号走线过长或靠近动力线都会引入噪声。FPGA的IO可配置模式比ARM MCU更灵活一些常见的有推挽输出、开漏输出、上拉/下拉输入、差分信号等。在FPGA里IO模式一般由综合工具或约束文件配置和ARM在寄存器层面配置的模式类似但FPGA的优势是IO数量多且时序可编程。如果FPGA驱动外部设备出错先检查引脚约束是否正确、输出类型是否匹配负载需求、IO电平标准是否一致。开漏输出要特别注意是否加上拉电阻否则高阻态时信号状态不确定这种问题在示波器上看起来就是电平漂移很难直接发现故障点。5. 常见问题速查与排查经验总结5.1 问题、症状、排查路径一览为了查阅方便我把日常最常遇到的问题整理成一个速查表给各位当参考症状可能原因首选排查命令/工具解决思路ping通但应用连不上端口未监听、防火墙拦截、服务绑定错误telnet、ss、nc确认端口监听地址检查防火墙规则连接建立很慢SYN重传、握手超时、负载高tcpdump抓包、sar分段确认SYN-ACK响应时间抓包找重传连接断断续续代理超时、空闲连接过期、防火墙回收mtr、tcpdump、服务端日志调整代理超时应用层增加心跳保活传输速率上不去TCP窗口太小、网卡队列不足、带宽限制iperf、ethtool、ss -i调大TCP缓冲开启网卡多队列CPU iowait高磁盘排队、日志刷写频繁、备份任务iostat、iotop、pidstat定位高IO进程错峰或限流jbd2高频写IO小文件元数据操作频繁、日志模式不合适iostat、strace、dmesg优化文件目录结构调整日志写模式应用读文件慢大目录遍历、缓存命中低strace、perf、vmstat优化目录结构加大缓存5.2 我踩过的坑和总结的避坑经验多数网络和IO问题排查到最后总结下来都是小问题但耗时的点往往在于方向错。我把自己在这方面的几条经验写出来第一条抓包前务必校准时间。服务器和客户端的时间偏差过大抓包分析时很容易误判请求顺序和超时逻辑。我的习惯是统一使用NTP同步抓包时保留时间戳分析时结合应用日志时间线。第二条不要盲目调整内核参数。网上有很多“性能优化建议”动不动就让你改TCP重传次数、增大socket缓冲区。但这些参数往往是牵一发动全身改小了可能导致连接不稳定改大了内存占用又上去了。改之前要弄清楚当前状态的瓶颈在哪个环节改之后还要用压力测试验证效果。第三条IO高不代表磁盘要换。很多磁盘IO升高其实是代码或配置造成的比如日志框架在每次请求都写一条同步日志、数据库批量更新没有走事务合并、文件系统没有启用合适的缓存策略。换磁盘是最简单的方案但往往是性价比最低的方案。第四条硬件IO问题优先检查接线和电气参数其次才是程序。在高频脉冲、开漏输出这些场景里信号质量问题比代码逻辑问题更容易被忽略。示波器量和程序review两个动作要同时做不要只在代码里查。5.3 再分享一个建议如果你在排查网络或IO问题时卡住了不妨换个角度把关注点从“哪里出故障”换成“数据流经了哪些环节每个环节的表现如何”。网络数据从客户端发出经过网卡、协议栈、内核缓冲、应用层、磁盘等等一步一个脚印地查总能找到卡住的那个点。这套思路和动手能力比记住任何一份命令清单都管用。