ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

网络与IO问题排查实战:分层定位、命令速查与性能分析

2026/9/16 8:00:27 拓冰建站 浏览量
网络与IO问题排查实战:分层定位、命令速查与性能分析 搞后端和嵌入式混着做了这么多年我最大的感受就是线上事故十个里有六七个出在网络上剩下四五个出在 IO 上而且这两类问题还经常互相掩盖。网络抖动导致应用超时应用超时引发线程堆积线程堆积反过来把磁盘和 CPU 打爆磁盘 IO 卡住又会让网络请求迟迟得不到响应从客户端看就是“网络慢”。所以这一章我想把平时排查网络和 IO 问题的一套实战方法论整理出来把我踩过的坑、验证过的命令、看过的指标都摊开讲讲。这事适合谁看后端开发、系统运维、嵌入式软件工程师还有那些被生产环境折磨过的朋友都能从中找到能直接用的排查路径。我不是给你背命令而是给你一套拿到问题之后能一步一步追下去的思考方式。毕竟命令可以随时查但排查的思路和手感才是真正值钱的东西。1. 网络排查先立框架分清“连不通”和“慢”是两条路线拿到一个网络问题先别急着执行命令。我习惯先问三个问题这是什么服务走什么协议最近改过什么很多问题不是凭空冒出来的大概率是发布、扩容、改配置之后才出现的。但也有例外比如磁盘满了导致服务假死这种就和网络本身没关系。把问题范围定下来再往下定位。网络问题的表象我粗暴地分成两大类一是连不通二是慢。连不通的排查路径是从物理层一层层往上排慢的排查路径重点在看链路质量、协议交互和应用层处理。两条路线用的命令和思路差异很大混在一起容易越查越乱。1.1 连不通的五层定位法我一直用的套路是从底层往上层一层层排每一层用最轻量的命令验证快速缩小范围。第一层确认物理链路和本机状态。先看网卡是否 upip addr 看状态ethtool eth0 看链路速率和双工模式dmesg 里有没有网卡报错。早年我遇到过网线松了一半链路时而千兆时而百兆表现就是传输速度忽高忽低这种不看物理层根本发现不了。第二层确认局域网内的连通性和 IP 配置。ping 同网段网关确认二层三层能通ping 不通就看 arp -a 有没有对端 MAC。如果 arp 有 MAC 但 ping 不通大概率中间有人配了访问控制。如果连 arp 都没有检查 VLAN 划分、交换机端口配置和网线插没插对。第三层确认跨网段路由和链路质量。traceroute 看路径经过哪些节点哪一跳开始丢包哪一跳延迟突然飙升。用 traceroute -T -p 443 可以带 TCP 端口探测很多网络设备会过滤 ICMPTCP 探测往往更真实。第四层确认目标端口是否开放并可以建立连接。telnet ip port 或者 nc -zv ip port 是最直接的验证方式能通就说明传输层没问题不能通就检查服务端口、防火墙规则和监听状态。ss -lntp 看本机端口监听情况在服务器上排查时先看服务是否真的起来了。第五层才是应用层协议交互。用 curl -v 看完整的 HTTP 交互过程包括 DNS 解析、TCP 连接、TLS 握手、HTTP 状态码一眼就能看出卡在哪一步。数据库连接连不上时mysql 客户端加上 --verbose 也能看到认证和握手阶段的具体进展。这套五层定位法的核心思想是二分排除。每次只验证一个问题要么向上要么向下几轮之后就能把故障点锁在很小的范围内。1.2 慢而不丢包的排查方向连不通排查起来相对简单真正难的是那种“能通但很慢”的问题尤其是丢包率几乎为零、ping 延迟也正常但业务数据传输就是慢的现象。这种情况我通常从四条线并行查。第一条线看带宽和吞吐。iperf3 是官方推荐的测速工具服务端 iperf3 -s客户端 iperf3 -c 服务端IP -P 4 -t 30测出来的带宽如果远低于物理链路标称值说明链路质量或者中间设备有什么限制。要注意网络测速结果和真实业务传输之间的差异iperf3 默认用的是大包但业务如果有大量小包表现会差很多。第二条线看链路丢包和重传。mtr 工具比 traceroute 更适合持续观察链路质量它会持续发送探测包并统计每个节点的丢包率。重点关注最后一跳之前的节点只有中间节点丢包而终点不丢通常反而是正常的因为中间节点低优先级处理探测包。真正要警惕的是终点也有丢包那说明链路确实有损耗。第三条线看 MTU 分片问题。这是一个很容易漏掉的坑TCP 握手可以成功小额数据发送也正常但一旦发送大数据包就卡住或者超时多半是 MTU 不一致。在能连通的基础上用 ping -M do -s 1472 目标IP 测试能否不带分片地发送大包如果超过某个值不通说明路径上有设备 MTU 设置偏小需要调整网络接口的 MTU 或者开启 PMTUD。第四条线看 TCP 协议栈和内核参数。TCP 窗口太小、缓冲区不足、拥塞控制算法不合适、连接处于 TIME_WAIT 状态过多都会让传输变慢。ss -tin 能看到当前连接的发送和接收队列、拥塞窗口和重传情况。如果重传率很高优先怀疑链路层而不是调内核参数。2. 8 个高频网络排查命令的实战用法网络排查的命令说来说去就是那么几个但同样的命令用法深了效果天差地别。我把自己平时用得最顺手的组合列出来每一个都给出具体场景不是光告诉你参数而是告诉你什么时候该用。2.1 连通性命令的进阶用法ping 不只是用来判断通不通它还能测 RTT、看丢包率、测 MTU。我常用的几个变体ping -c 100 -f 目标IP 做洪水模式的持续 ping看长时间运行的丢包率ping -M do -s 1400 目标IP 测路径 MTU从 1500 开始逐步减找到最大的可用包长再算上 IP 头 20 字节和 ICMP 头 8 字节就是实际的 MTU 值。nc 是排查端口问题的主力nc -zv 目标IP 端口 可以快速判断端口开放情况nc -vz -w 3 设置三秒超时避免了某些环境探测时长时间卡住的问题。批量检查多个端口时写个小循环或者用 nmap -p 端口范围 目标IP比一个个敲 nc 要高效得多。ss 命令要完全替代 netstat 成为首选。我常用的 ss -lntp 看本机监听端口ss -tn state established 看已建立的连接ss -tin 看连接的详细统计特别是发送队列 Send-Q 和接收队列 Recv-Q。Recv-Q 持续不降说明应用没有及时读取数据问题在应用层Send-Q 持续堆积说明对端处理不过来或者网络拥塞。curl 是我看 HTTP 服务状态的首选工具加 -w 参数可以输出详细的时间分解。我常用的格式比较长把 DNS 解析时间、TCP 连接时间、TLS 握手时间、首字节时间和总时间都打印出来一眼就能看出请求耗时花在哪个阶段排查“接口慢”这类问题非常管用。2.2 用抓包和协议分析工具看真话命令输出告诉我们的都是“间接判断”只有抓包看到的才是实实在在的报文交互。tcpdump 是服务端排查的首要工具我用的固定套路是 tcpdump -i any -s 0 -nn -w /tmp/capture.pcap host 目标IP and port 端口详细记录每一个数据包并落盘保存之后再慢慢分析。一定要加 -nn 参数不然 tcpdump 会做反向 DNS 解析既慢又噪音大。抓下来的包我习惯用 Wireshark 打开分析它比终端文本日志直观得多。Wireshark 自带的统计功能非常强Protocol Hierarchy 能看到流量构成Conversations 能看哪个连接在占用带宽Follow TCP Stream 能还原一次完整的 TCP 会话内容。还有个很实用的功能是分析 TCP 重传和乱序包在 Expert Information 面板里会直接标注出来排查“网速慢但看不到明显瓶颈”这种问题时会省很多力气。如果你和我一样经常在服务器上用命令行临时分析抓包文件tshark 是 Wireshark 的命令行版本可以直接在终端里做条件过滤和统计。比如 tshark -r capture.pcap -q -z conv,tcp 能统计所有 TCP 会话的流量大小定位谁在占带宽。网络调试助手这类工具也有它的用武之地特别是排查 UDP 通信问题时它可以模拟客户端发一组预设报文观察服务端的响应快速验证 UDP 端口通不通、协议解析是否正确。我做嵌入式设备调试时经常用网络调试助手直接和设备通信比写完整的测试程序快很多。但要注意抓包文件里可能包含敏感数据用完记得及时删除别把抓包文件随手丢在共享目录里。3. IO 性能问题排查方法论IO 问题比网络问题更隐蔽因为它不像网络那样有清晰的分层模型。服务器慢、数据库慢、文件读写慢背后可能都是 IO 问题。IO 问题可以发生在磁盘、文件系统、内核块设备层、设备驱动、硬件链路每一层的症状和排查方法都不一样。3.1 三个指标先定位util、await、io 队列拿到一个“系统很慢”的反馈我第一个动作就是 iostat -x 1 2在 1 秒间隔下采样两次输出扩展指标。重点看三个指标%util、await、aqu-sz。%util 表示设备有 IO 请求的时间占比。很多人看到这个数字高就认为磁盘满了其实不完全对。它只表示设备在工作不代表已经达到性能上限还得结合队列长度和等待时间综合判断。await 是 IO 请求的平均处理时间包括等待时间和处理时间用银行排队来类比await 是你从进银行到办完业务离开的总耗时。aqu-sz 是平均队列长度这个值高说明请求在排队。我的判断逻辑是如果 %util 很高、await 很低说明设备忙得过来问题可能是请求量本身就大这时候考虑加缓存或者限流如果 %util 不高、await 却很高大概率有排队、锁冲突或者磁盘本身有问题像坏道重试、RAID 降级这些都会导致 await 飙升如果 aqu-sz 一直在往上走但 %util 稳定说明请求堆积的速度超过了处理速度。还有一个指标值得关注就是 r/s 和 w/s 每秒读写请求数以及 kB_read/s 和 kB_wrtn/s 吞吐量。把这几个指标结合起来能判断是 IOPS 瓶颈还是带宽瓶颈。大量小文件随机读写的场景最先到极限的往往是 IOPS大文件顺序读写场景最先到极限的往往是带宽。3.2 从“谁在读写”到“为什么读写”的定位链条iostat 能告诉你系统层面的 IO 状态但它不会告诉你哪个进程在造成 IO 压力。定位链条下一环是找到罪魁祸首然后再回答它为什么在读写。iotop 可以实时看到各个进程的磁盘读写速率是定位 IO 大户的首选工具。有些环境默认没装apt install iotop 或者 yum install iotop 就能搞定。输出里重点关注 TID、进程名、IO 百分比如果某个进程 IO 占比长期超过 50%就要深挖了。知道是哪个进程之后用 lsof -p 进程号 看它打开了哪些文件用 ls -l /proc/进程号/fd 也能看到同样的信息。如果看到大文件、日志文件或者临时文件在频繁读写基本能判断出原因。如果还需要更详细的信息strace -p 进程号 跟踪系统的 read、write、fsync 调用能看到每次读写的文件描述符、大小和时间戳。但 strace 对性能影响很大生产环境慎用最好在问题发生的短时间内快速抓一把就停。往下走到内核块设备层blktrace 可以记录每个 IO 请求从进入块层到完成的全过程能看到请求的插入顺序、合并情况、完成时间。这个工具一般人用得少但遇到让人摸不着头脑的 IO 问题它能提供最底层的信息。需要注意的是 blktrace 需要 root 权限而且会产生大量数据生产环境使用时务必控制抓取时长。我处理过的一个 jbd2 IO 过高的典型案例很有代表性。jbd2 是 ext4 文件系统的日志提交线程它的任务是把文件系统的元数据变更写入日志区。某次发现磁盘 util 一直在 90% 以上但 iotop 里看到 top 的读写并不高最大 IO 消耗者竟然是 jbd2。后来排查下来是某应用在大量创建和删除临时文件每次目录操作都要触发元数据更新文件系统把每一次修改都同步提交到日志IO 就被打满了。解决方向是优化应用的临时文件使用方式比如改用内存文件系统或者调整挂载参数降低 fsync 频次。4. Java IO/NIO 与网络 IO 的经典问题模式做后端服务的人有很大概率会遇到和 Java IO 相关的故障。Java 的 IO 模型从 BIO 到 NIO 再到 AIO每一步演进都是为了应对不同的并发场景但在实际投产中每一种模型都有它特有的问题形态。4.1 BIO 阻塞在什么地方一查便知传统的 BIO 模型下一个线程负责处理一个连接线程阻塞在读和写上面。当调用 read 时如果对端一直没有数据线程就会一直卡住。jstack 打印出来的线程堆栈里如果看到 java.net.SocketInputStream.socketRead0 这个栈帧就说明线程正阻塞在读取网络数据上这种情况在低并发时没什么问题但连接一多线程数就会迅速膨胀。我排查过一个经典事故某个服务在并发升高后整体响应时间暴涨线程数一路飙升到几千CPU 被上下文切换占满。看 jstack 后发现大量线程都阻塞在 socketRead0对端客户端不发送数据也不断开连接服务端却在等待数据连接被占住不释放。本质上是不合理的 BIO 使用方式叠加了连接泄漏。解决办法是给 socket 设置 read timeout让等待时间有上限同时排查客户端连接未被正确关闭的问题。磁盘 IO 阻塞在堆栈里也很有特征。如果看到 FileInputStream.readBytes 或者 FileChannel.read并且后续线程栈停在文件系统相关的系统调用上那就是磁盘响应慢拖住了业务线程。这种问题单靠 Java 层的优化解决不了要回到第 3 章的 IO 指标排查思路里找根因。4.2 NIO 下“假死”问题的排查NIO 解决了 BIO 线程膨胀的问题但又带来了新的故障形态。最常见的是服务表现为假死端口还在监听TCP 连接也能建立但请求就是不返回。这时候用 jstack 看线程发现业务线程并没有阻塞在 read 上而工作线程大多处于 RUNNABLE 或者 WAITING 状态。排查这类问题我会先确认事件循环线程是否还在正常工作。如果用 Netty检查对应的 EventLoop 线程堆栈如果它长时间停留在某个用户代码里说明某个 handler 里的逻辑阻塞了事件循环导致后续所有事件都无法处理。我遇到过一次很典型的案例某 Netty 服务的 worker 线程占用率持续 100%抓线程栈发现业务代码在 handler 里做了一次同步的数据库查询数据库执行超时网络线程全被拖住整个服务就“假死”了。NIO 场景下还常见一类和连接生命周期相关的报错比如 stream disconnected before completion、peer closed connection with。这类错误的本质是对端关闭了连接但本端还在继续发送或等待数据。服务端最常见的触发原因是设置了空闲超时或者读取超时客户端没在超时时间内发送数据服务端主动把连接关了。排查思路是看日志里连接建立的时刻和断开的时刻计算两侧的空闲时间对比超时配置基本能找到问题。另外一个容易被忽视但后果严重的问题是 Java 里面 NIO 的 ByteBuffer 内存泄漏。堆外内存被分配后没有正确释放表现就是服务内存看着不高但进程的 RSS 内存持续增长最后被系统 OOM Killer 杀掉。排查这种问题要用 jcmd 或者 Java Flight Recorder 分析堆外内存的使用情况重点检查使用 DirectByteBuffer 的代码路径是否存在未释放的情况。5. 嵌入式场景下的 IO 口问题排查前面聊的 IO 是服务器的磁盘和网络 IO但还有一个完全不同的“IO”——单片机和嵌入式系统里的 GPIO 输入输出。做嵌入式开发时IO 口相关的问题几乎会陪伴你整个开发周期。这类问题虽然和服务器 IO 是两码事但排查思路完全是相通的先确认配置再确认信号最后确认负载。5.1 推挽、开漏、上拉到底怎么选GPIO 的输出模式最常见的是推挽输出和开漏输出。推挽输出可以主动输出高电平和低电平驱动能力强。比如驱动 LED 灯、蜂鸣器用推挽模式加一个限流电阻就足够了。开漏输出只能主动拉低电平高电平要靠外部上拉电阻拉到高这种结构的好处是可以实现“线与”逻辑多个开漏输出可以直接并联任何一个拉低总线就是低电平。I2C 通信协议就是使用开漏输出的典型场景。上拉和下拉电阻解决的问题是输入引脚悬空时电平不确定。一个按键一端接 GND另一端接 GPIO如果让按键按下时 GPIO 读到低电平那就必须给 GPIO 配置一个内部上拉电阻让按键没按下时引脚默认是高电平。如果不配置上拉引脚悬空时读到的电平可能是随机的会出现按键偶尔误触发的诡异现象。这个问题在 ESP8266 和 STM32 上表现都一样。以 ESP8266 的 Arduino 环境为例设置引脚模式为 INPUT_PULLUP 就是启用了内部上拉。STM32 的 HAL 库则要配置 GPIO_InitStruct.Pull 为 GPIO_PULLUP 或者 GPIO_PULLDOWN。很多新手忘记配上下拉就会出现传感器读数漂移、按键误触发、通信偶发失败这些十分难缠的问题。5.2 IO 口异常排查的现场经验嵌入式 IO 问题最常见的几个症状我都踩过。第一个症状是“电平读了变来变去”排查方向是确认上下拉配置、检查引脚是否悬空、是不是和别人共用了引脚。第二个症状是“输出驱动不了设备”比如 GPIO 直接接继电器线圈电压被拉低继电器吸合不了。这种情况要先查数据手册里 GPIO 的最大输出电流别直接用推挽输出驱动大负载必要时加三极管或者达林顿管驱动。第三个症状是“开漏输出没有高电平”确认是不是漏了上拉电阻或者上拉电阻选得太大导致信号上升沿太慢。I2C 总线的上拉电阻一般取 2.2k 到 10k 之间具体要根据总线速度和负载电容选。电容负载大的时候电阻选太大会导致边沿过缓设备识别不到。示波器在这种场景下比万用表有用得多能看到完整的边沿波形。扩展 IO 口也是嵌入式开发中经常做的操作常用的方案有 74HC595 串行转并行、PCF8574 通过 I2C 扩展 IO。排查这类问题我的顺序是先确认通信链路正常用 I2C 扫描工具看设备地址能不能扫到再确认时序符合芯片手册要求特别是时钟频率和建立保持时间最后再确认片选、锁存这类控制信号的电平逻辑是否匹配。很多时候定位到最后问题出在控制信号极性搞反了。6. 问题排查速查表与工具清单排查网络和 IO 问题最怕的就是拿到一个问题凭感觉东试一下西试一下。最后这一节我把常见症状和对应方向整理成一张速查表顺便列出我平时常用的工具和行为习惯。6.1 常见症状与排查方向对照表症状可能方向首查命令 / 工具备注目标主机 ping 不通物理链路、IP 配置、ARPip addr、arp -a、ping先确认本机网络状态是否正常端口连不上服务未启动、防火墙拦截ss -lntp、nc -zv排查顺序先监听后防火墙网络延迟高链路质量、重传、拥塞mtr、ping -f、ss -tin观察持续一段时间避免偶发误判带宽打不满MTU、TCP 窗口、中间设备限速iperf3、ping -M do分大小包测试对比结果HTTP 响应慢DNS、TCP、TLS、应用处理curl -w 时间格式用时间分解定位耗时阶段磁盘 %util 高业务请求量大、后台任务iostat -x 1结合 await 一起看避免误判磁盘 await 高坏道、RAID 降级、锁竞争iostat、dmesg检查系统日志中是否有磁盘报错IO 队列堆积请求量超过处理能力iostat、iotop定位到具体进程再处理jbd2 占用高元数据更新频繁、fsync 过多iotop、strace优化应用写入模式客户端卡在 socketRead0对端不读不写、超时未设置jstack设置 socket read timeoutNIO 服务假死handler 阻塞、内存泄漏jstack、jcmd重点看事件循环线程GPIO 电平漂移上下拉配置缺失、悬空万用表、示波器检查引脚复用和外部电路I2C 通信失败上拉电阻、地址错误I2C 扫描工具、示波器确认地址再确认时序6.2 我的工具和行为习惯清单软件工具我分为三层第一层是快速判断层ss、ping、nc、curl、iostat、iotop第二层是深度分析层tcpdump、Wireshark、tshark、strace、blktrace第三层是模拟验证层iperf3、网络调试助手这类工具用来构造特定的流量测试场景。每台我会长时间维护的服务器上这些工具都会提前装好避免问题发生时再去装那样会耽误黄金排障时间。再分享几个我认为很重要的行为习惯。一是做基线系统和服务的正常状态指标一定要提前记录比如正常时磁盘 await 是多少网络延迟是多少没有基线就谈不上异常。二是小步变更排查问题时一次只改一个变量改完观察再改下一个这个习惯能避免引入新的问题。三是留记录每次排障结束把现象、排查路径、根因和最终方案整理成文档。这不是做给领导看的是给自己下一次排障用的。我个人在实际操作中最深的体会是排障时最忌讳猜最有效的是按层次收集证据。网络就按物理层、链路层、网络层、传输层、应用层一层层排IO 就按系统指标、进程行为、系统调用、块层一个个往下追。当你能熟练地把一个模糊的“系统很慢”逐步拆解成具体的指标异常时问题其实已经解决了一半。这章的内容可能不能覆盖所有锅但掌握了这套分层排查的思维大部分网络和 IO 问题都不会让你手足无措。