ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

网络与IO问题排查实战:从现象到根因的联合诊断思维

网络与IO问题排查实战:从现象到根因的联合诊断思维 网络和IO问题越往后学越会发现它们总是手牵手出现。客户端日志报一句stream disconnected before completion很多人第一反应是网络断了结果抓包一看服务端磁盘队列早就爆了线程卡在读写里出不来连接被对端强制关闭。这种场景我见过太多次所以看到第6章 网络与IO问题排查实战这个标题时我希望你别把它理解成两个独立章节的拼盘而是一套需要联合使用的排查思维。这套内容适合谁看后端开发、运维、工控、嵌入式工程师都该看。你不需要是网络专家也不用是内核高手跟着排查思路走一遍至少能把问题到底出在哪一层这个问题回答清楚。我会从现象到根因的角度把网络IO的常用命令、抓包思路、指标解读、硬件IO交叉问题逐一展开配上几段真实排查案例方便你直接对照自己的环境去用。1. 排障的第一步不是敲命令而是先给卡顿定性1.1 通而不快和快而不稳是完全不同的两件事很多人一上来就 ping。但卡顿这个词信息量太少了。业务侧报卡可能是网络时延高可能是带宽不够可能是应用线程池耗尽可能是磁盘IO慢导致数据库查询超时甚至可能是GC停顿。ping反映的只是ICMP层的往返延时它最多帮你确认链路通不通、有没有明显丢包连TCP层的状态都看不到更不要说应用层和IO层。所以第一步一定是把现象问细是偶发性的卡还是持续性的慢偶发多半和丢包、重传、中断、GC有关持续则更可能指向带宽打满、队列堆积、CPU或磁盘瓶颈。是单机问题还是全网问题单机看系统资源和连接状态全网要看交换路由、DNS、出口带宽。是某个端口不可用还是整机网络不可达端口问题看四层整机不可达看链路和IP。配合这组问题我通常会先记一份现状快照再动手包括当前连接数、CPU、load、内存、磁盘读写、网络吞吐、进程线程状态。这不是浪费时间而是防止排到一半忘了原始状态。很多排障翻车都是因为只顾着追一个指标把真正的现场丢了。1.2 一次深夜告警的完整推演问题是从网络层滑到IO层的打个比方排障就像医生看病先看体温血压再决定做CT还是查血。有个案子我印象很深凌晨两点接到告警一个内部服务响应时间从10ms飙到2s超时率持续上升。第一波排查当然是网络ping 目标机延迟1ms零丢包。telnet 目标端口能通但握手耗时异常稳定在500ms以上。抓包发现三次握手的SYN-ACK返回极慢连接并没有被丢弃只是协议栈来不及响应。到这里如果只盯网络大概率会怀疑交换机或防火墙。但仔细看端到端延迟发现所有进入该主机的连接都慢问题更可能出在主机自身。于是转到IO视角iostat 一看磁盘 util 长时间100%await 已经到几千毫秒再看 /proc/interrupts网卡中断几乎都落在同一个CPU核上而这个核同时还承担着大量磁盘中断处理。磁盘层卡死后网卡驱动向协议栈提交数据包也拿不到内存和锁最终表现就是握手慢、连接卡。根因最后查出来是某条磁盘RAID组的缓存策略异常加上一块硬盘离线触发了全盘重建。这件事给我的教训是网络快不等于服务快网络慢也不一定就是网络背锅。TCP连接握手慢必须往主机资源、IO栈多看一眼。1.3 为什么实战中必须把网络和IO放在同一张排查表里如果把网络和IO分别当两本书看排查时很容易出现分工断层网络工程师说链路没问题开发说代码没问题DBA说数据库没问题最后问题悬在那里。真正高效的团队手里应该有一张统一的排查表把指标按层排布链路层、IP层、TCP/UDP层、Socket层、应用层、文件系统层、块设备层、驱动层。每一层各看什么指标我先给你一张精简版层次核心指标常用命令链路层丢包率、双工模式、错误包计数ethtool -S、ip -s linkIP层重传、乱序、TTLnetstat -s、tcpdumpTCP层握手耗时、重传率、连接队列ss -st、tcpdumpSocket层ESTABLISHED/TIME_WAIT数量、读写缓冲区ss -antlp文件系统缓存命中、脏页比例/proc/meminfo、iostat块设备util、await、队列长度iostat -x 1驱动/中断中断分布、软中断CPU占用/proc/interrupts、mpstat这张表不是给你背的是让你在接到告警时能按图索骥地缩小范围。下文每一层都会展开讲。2. 网络层排查从通不通到快不快的两级台阶2.1 连通性验证ping、telnet、nc怎么配合才不踩空基础命令用得好能省掉大量抓包的功夫但前提是不能单打独斗。ping是起点但只是起点。一个经典误区是ping通了就认为网络正常。实际上很多主机在防火墙里放行了ICMP却封掉了TCP端口或者TCP代理层已经异常。所以我的标准组合是三层验证。第一层先确认链路和路由ping -c 10 目标IP # 看丢包率和RTT分布 mtr -r -c 10 目标IP # 看每一跳的丢包位置mtr 比 traceroute 好用得多它会持续探测并输出每一跳的丢包率。如果只有中间某一跳丢包但后面不丢往往说明中间节点的ICMP限速不一定是真实故障如果最后一跳和倒数第二跳同时丢包才更接近真正的链路问题。第二层再确认端口和应用协议telnet 目标IP 端口 # 简单粗暴能通就进黑洞 nc -vz -w 2 目标IP 端口 # 更可控的端口探测 curl -v http://目标IP:端口/ # 带上协议细节的探测nc -vz适合脚本化批量检测curl -v能直接看到TLS握手、HTTP响应头判断是连接层卡住还是应用层卡住。遇到仅内网有问题的环境我还会加一条curl --resolve指定Host排除DNS干扰。第三层确认双向传输质量。这就是后面要说到的测速和抓包。三层都过了才能说网络大体没问题。2.2 带宽与时延网络测速工具的内外网适用边界很多人搜网络测速在线测网速但这类网页测速工具只能说明你的出口带宽到测速节点的表现不能用来定位内网问题。内网带宽、延迟、丢包推荐用 iperf3 说话。iperf3 的用法分两端服务端先起iperf3 -s -p 5201客户端打流量单线程和并发各一次这样可以看带宽是受限于单连接窗口还是链路容量本身iperf3 -c 服务端IP -p 5201 -t 30 -i 1 # 单线程 iperf3 -c 服务端IP -p 5201 -t 30 -P 8 # 8并发看结果时不要只盯最后的带宽数字重点关注重传。iperf3 最后几行会输出 retransmit 计数如果重传率高于1%甚至更高说明链路里有拥塞或质量问题实际传输速度上不去也就有了解释。不然你只会看到带宽只有标称的一半却不知道是协议效率问题还是物理链路问题。如果怀疑MTU设置有问题用 ping 带 DF 位一口口试ping -M do -s 1472 目标IP # 1500-28标准以太网MTU下的最大载荷 ping -M do -s 1452 目标IP # 1450常见隧道和VXLAN场景能通的最大值加上28就是你当前路径实际可用的MTU。两边网卡都设1500中间却跑着GRE隧道出现过大量握手可以、传输卡死的问题基本都是一口口试出来的。2.3 抓包看TCP握手、重传、RST背后的信号抓包工具首选 tcpdump很多环境没有Wireshark GUI但它配合-w存文件再拉回本地分析非常方便。排查一次连接是否正常常用命令是这样的tcpdump -i eth0 -nn host 目标IP and port 端口 -s 0 -w /tmp/dump.pcap抓到 pcap 后在 Wireshark 或 tshark 中看三个关键点。第一握手阶段花了多久。如果SYN发出后迟迟等不到SYN-ACK问题通常在对端或者中间防火墙如果SYN-ACK回来了但ACK发不出去或对端收不到要怀疑本机路由、连接跟踪表满、或对端主机负载过高。第二重传率。tshark -r dump.pcap -q -z io,stat,0可以看整体统计重传包占比高的场景业务层会表现为慢但不断。常见原因是双工不匹配、网线质量差、驱动缓冲区太小。第三RST包出现的位置。TCP里RST不是网络故障而是有人主动拒绝。比如对端端口没监听、防火墙主动拦截、应用层调了SO_LINGER关闭都会产生RST。我见过一个很典型的场景WebSocket客户端报stream disconnected before completion: failed to send websocket request: io随手抓包发现HTTP 101响应发出后服务端立刻发了RST应用层才报了IO类错误。这里的IO错误其实是连接被服务端主动关闭后的映射跟物理链路半毛钱关系都没有真因在服务端读超时和线程池排队。3. IO问题定位从iowait到中断逐层逼近瓶颈3.1 iostat/pidstat/strace的组合用法IO性能明显下降了这个热搜词挂了很多次合适的回答方式是把IO拆开看。磁盘IO、网络IO、内存映射IO表现完全不同。系统层面的第一个落点永远是 iostatiostat -x 1 5输出里重点挑这些字段看%util、r/s、w/s、r_await、w_await、aqu-sz、svctm。%util接近100%说明设备持续繁忙但注意SSD特性不同util 100%不一定就是瓶颈要配合await看。再进一步用 pidstat 找到谁在读写pidstat -d 1 5它会显示每个进程的KB_rd/s、KB_wr/s和每秒读写次数。如果发现某个进程读写尖峰异常再用strace -p PID跟踪它的系统调用重点看 read/write 的返回值和耗时。但注意生产环境不要长时间strace会严重影响性能一般抓几秒就停。3.2 磁盘队列与中断IO性能下降的指标怎么读我见过不少团队一看到iostat里 %util 高就急着加机器其实加机器解决不了问题。要分清两种典型情况。一种是await高但aqu-sz平均队列长度低说明单次IO本身就慢要么磁盘老化、要么被其他I/O挤占、要么RAID组在重建另一种是aqu-sz很高、%util也高同时r_await、w_await正常但排队严重说明是请求量太大这个才会考虑扩容或限制并发。还有一个经常被忽略的指标是 /proc/interrupts。网卡和磁盘控制器的中断如果集中在一个CPU核上会导致软中断把单个核打满其他核闲着整个系统吞吐照样上不去。出现这种情况优先考虑中断亲和性设置用 irqbalance 或者手动绑定比盲目升级CPU更有效。排查IO问题时也要看内存层。脏页比例过高、回收不及时会让写盘看起来像网络抖动。这时候查 /proc/meminfo 里的 Dirty 和 Writeback 值再留意系统日志里有没有blocked for more than 120 seconds之类的内核信息。这种问题只用iostat会漏掉因为瓶颈可能在内存回写而不是设备层。3.3 Java服务里IO与NIO的排查差异很多人搜java io和io nio。传统BIO模型里每个连接占一个线程阻塞在read上NIO模型用少量线程管理大量连接。两者排查时候的注意力完全不同。BIO服务崩溃或者变慢先在 jstack 里看线程栈如果大量业务线程卡在java.net.SocketInputStream.socketRead0说明连接已经建立但对端迟迟不响应。这时候查网络抓包很可能看到一堆连接处于半开状态。问题根源也许是服务端线程池满也许是防火墙空闲超时把连接清掉了但应用不知道。NIO服务虽然不阻塞线程但问题会转移到Selector和ByteBuffer。排查时看jstat的GC频率GC停顿会引起NIO读写的批处理延迟再看Netty等框架的线程池状态如果IO线程出现频繁的慢任务整个EventLoop都会跟着卡。这类问题单开网络视角很难看到全貌必须把线程、GC、网络事件三者放在一起看。4. 交叉场景日志中的网络断开与IO堆积4.1 从stream disconnected看连接关闭的真实含义很多框架报错里都带IO两个字比如stream disconnected before completion: io error: peer closed connection但它说的未必是物理网络坏掉了。这句话翻成大白话是我在读写数据流的过程中链路被意外关闭了。导致关闭的原因可以是一个RST可以是对端正常FIN后我们又发了数据也可以是连接空闲超时被中间设备掐断。判断方法只有一条抓包。抓包前先确认时间点最好把应用日志里的报错时间记录下来然后按时间窗口过滤看那几秒发生了什么。tcpdump -i any host 目标IP and port 端口 -s 0 -w /tmp/ws.pcap看包的顺序是谁先发了FIN谁先发了RST这个时间点之前有没有长时间的空闲。如果是对端先RST看它的前一个操作往往是因为读超时如果连接空闲了很久才被断开多半是NAT会话超时或服务端keepalive设置太短。我想强调一下很多Wireshark新手看到TCP RST就认为是网络攻击或异常其实RST在合理场景里是正常的协议行为。关键看RST的上下文以及它是在明确Time Wait之后由哪一方发起的。4.2 磁盘IO瓶颈如何伪装成网络故障网络报了高延迟系统load也没高但就是服务慢。这类案子里磁盘IO是藏得很深的一个元凶。因为网络延迟的统计经常是从客户端发请求到收到响应的时间这期间包含了服务端读取磁盘、处理数据、发出响应的全过程。只要磁盘读写慢客户端看到的就全是网络延迟。有一次排查一个文件上传服务客户端反馈网速特别慢20MB的文件传了一分钟。内网iperf3测出来带宽1Gbps完全正常。后来看服务端iostat上传接口每收到一段数据就要做一次fsync落盘磁盘的每秒事务数直接打到上限整体等待时间全耗在同步写上了。优化办法是把同步落盘改成异步批量写同时调整文件系统的挂载参数。所以说网络快不快很多时候取决于网络两端接住数据的能力。遇到网络测速正常但业务上传慢一定记得去测试接收端磁盘的写性能。4.3 UDP通信与网络调试助手工控场景的常见坑两台电脑UDP通信使用网络调试助手是很多工控、嵌入式初学者的入门路径但UDP调试有几个比TCP更隐蔽的坑UDP没有握手调试助手显示发送成功只代表数据交给了本机协议栈不代表对端收到了。防火墙默认可能丢弃UDP尤其Windows环境第一次UDP数据包经常是被NetBIOS和防火墙策略丢弃的。UDP报文有长度上限超过MTU又没开分片会被直接丢弃开分片后又可能因为重组超时而丢包。用网络调试助手做UDP联调建议先在两端同时开Wireshark抓包确认包的流向。发送端看到了包只是第一步接收端是否真的收进应用层还要看应用层日志。另外工控场景大量使用UDP广播和多播一旦网络里有环路广播风暴会比TCP场景更快拖垮整个网络。所以排查UDP问题时我会顺手看一下交换机端口上的广播/组播包计数如果广播流量异常高先查环路。5. 硬件IO与网络的纠缠从摄像头到嵌入式5.1 海康相机IO触发抓拍为什么会引起网络卡顿海康相机怎么IO拍照一问很多通常是用IO口接外部传感器传感器给一个电平信号相机就触发拍照并上传。问题常出在触发瞬间传感器信号线没有隔离和滤波导致相机IO输入干扰触发一响相机开始连续抓拍超大图片码流突增这时候网络再被一路ONVIF/RTSP吃带宽整个交换机的下行端口就可能拥塞。处理思路分两边。硬件侧给IO信号加光耦隔离线缆用双绞屏蔽线设备侧良好接地避免电平毛刺造成误触发。软件侧调整抓拍策略限制连拍数量控制图片尺寸开启子码流预览。排查时用iftop或者交换机端口统计先看是哪个IP在触发瞬间把带宽吃满再顺着找到具体是相机主码流还是录像回放。这类问题最怕只改软件不改硬件。你只要把触发频率调低了问题好像消失哪天传感器信号又来一阵毛刺误触发风暴会再次把网络打崩。所以硬件隔离和滤波是必须做的一道防线。5.2 ESP8266扩展IO口后的稳定性排障esp8266扩展io口是老问题了但很多新玩家会忽略扩展IO与无线WiFi的干扰。ESP8266的GPIO本身复用多种功能扩展IO如果用了较高的翻转频率信号线就像一根小天线把2.4G频段的噪声抬起来WiFi RSSI明明很好丢包率却暴涨。排查方法是先把现象拆开拔掉所有扩展IO线只跑WiFi吞吐看是否正常然后一根一根接回每接一根就测一次。如果接到某根线后丢包率上升就把这根线的翻转频率降下来或者加串联电阻和上拉/下拉电阻减少边沿抖动。还有一个细节GPIO不能同时复用为ADC和普通IO做扩展之前要认真看芯片的复用表不然IO状态随机变化程序逻辑会在网络通信里表现为各种奇怪的超时。另外ESP8266做IO扩展时一定要给模块单独供电不要跟电机、继电器共用电源。电源纹波一大WiFi射频部分会跟着受影响表现就是网络间歇性连不上。嵌入式里太多不明原因的网络闪断最后都查回到电源和地线上。5.3 FPGA的推挽、开漏与上拉电阻对通信可靠性的影响fpga的io有没有类似arm的模式推挽、开漏、上拉这个问题本质上问的是IO驱动能力与总线稳定性。FPGA的IO确实可以配置成多种模式不同模式直接影响通信质量。推挽输出能主动驱动高电平和低电平驱动能力强适合点对点的高速信号但多条推挽输出直接并联就会冲突所以多设备共享总线时通常不用推挽。开漏输出只能主动拉低高电平靠外部上拉电阻适合I2C这类多设备共享总线。上拉电阻选得太小功耗高选得太大信号上升时间太长高速通信时会误码。I2C在上拉电阻选错时会表现得非常诡异低速能通高速就不稳定偶尔还会把设备锁死。如果FPGA通过IO扩展出来的信号直接影响网络设备比如复位芯片、PHY的MDIO引脚这些总线时序问题最终都会表现为网口不通、协商失败或者偶发断连。排查这类现象时不要只盯网络协议用示波器看一下MDIO/GPIO的上升沿和信号质量往往比抓一万个网络包更高效。6. 工具链与工作习惯让排查从救火变成打猎6.1 我常用的网络与IO诊断命令组合把常用命令整理成几组排查时按场景选比临时百度快得多场景命令组合连通性路由ping、mtr、arp -an、ip route get端口连接状态nc -vz、ss -antlp、ss -s应用层协议curl -v、openssl s_client、dig trace网络抓包tcpdump、tshark -r带宽/时延iperf3、ping -M doIO状态iostat -x 1、pidstat -d 1、strace -p中断/CPUmpstat -P ALL 1、cat /proc/interrupts连接跟踪cat /proc/net/nf_conntrack、sysctl net.netfilter.nf_conntrack_max每一条命令都值得自己先在测试环境跑一遍看看输出长什么样。真正出问题时脑子里要能浮现出哪条命令看哪个输出。这比记住任何一条命令的参数都重要。6.2 基线数据的价值没有参照物的告警等于没告警排查过几次之后你会发现很多异常其实很难说清因为没有基线。延迟从2ms涨到5ms到底算不算问题如果平时一直是5ms这就是正常如果平时稳定在1ms以内那5ms就值得追查。所以平时把关键指标采集下来比故障当天临时找工具重要得多。最简单的做法是部署一个带历史数据的监控系统Prometheus node_exporter Grafana 就够用了磁盘、带宽、中断数、socket状态都能采集。再懒一点至少把每天的iostat -x 1 5、ss -s这类命令输出存到日志目录里。等到出问题时翻出来一对比真相往往一目了然。很多团队花大价钱买智能告警不如先把基线数据老老实实存上半年。6.3 一次排查的复盘记录该怎么写最后想分享一个我自己的习惯。每次排查完一个网络或IO问题我都会写一份短复盘固定包含四块现象和时间线从告警触发到恢复的完整时间线精确到分钟所有采集到的现场数据包括命令输出、抓包文件、关键日志片段命名好存起来根因和依据当时是怎么从现象一步步走到根因的中间排除过哪些假设后续改进项比如监控补充、配置调整、代码改造。写这份东西不是为了交差是为了下一次同类问题出现时可以快速翻出来对照。很多人排障慢不是技术不够而是重复踩同一个坑。有了自己的知识库再遇到stream disconnected或者io性能明显下降这类信息量有限的报错你就能直接跳到关键检查项而不是从零开始猜。有个细节我得单独提一句复盘记录里一定要写清当时没做什么。实际排查中排除掉的假设和验证过不成立的方向跟最终根因一样有价值。我见过太多类似的网络问题反复出现就是因为前一次排查中不是这个原因的结论没有留下来下一个人来了又沿着同样的弯路走了一遍。
返回列表