| |
|---|
| 适用场景 | TCP 连接间歇性丢包、延迟突增、偶发超时,但无法明确根因 |
| 症状 | ping 正常,但 HTTP/SQL/RPC 请求超时率 1-5%;再次重试成功 |
| OS 版本 | RHEL/CentOS 7.9+ 或 Ubuntu 16.04 LTS+ |
| 内核版本 | Linux Kernel 3.10+ (推荐 4.15+) |
| 网络工具 | tcpdump、wireshark、netstat、ss、iperf3 |
| 资源规格 | |
| 网络要求 | |
| 权限要求 | root 权限(tcpdump 需要 CAP_NET_ADMIN) |
| 技能水平 | 高级运维工程师,熟悉 TCP/IP、内核参数、网络诊断 |
反模式警告
⚠️ 以下场景不推荐按本方案排查:
- • 症状:只有特定应用超时,ping 和 iperf3 正常
- • 改进:联系网络运营团队,用 mtr/traceroute 定位
- • 原因:LB/NAT 连接 timeout、Session 不同步
- • 改进:检查 NAT 超时设置(通常 300-600 秒)
替代方案对比:
环境与版本矩阵
| | | |
|---|
| OS 版本 | | Ubuntu 16.04 LTS / 20.04 LTS / 22.04 LTS | |
| 内核版本 | 3.10.0-1160+ / 4.18.0-305+ | 4.15.0+ / 5.10.0+ / 5.15.0+ | |
| tcpdump 版本 | | | |
| wireshark 版本 | | | |
| netstat/ss 版本 | net-tools 2.0+ 或 iproute2 4.9+ | net-tools 2.0+ 或 iproute2 4.9+ | |
| iperf3 版本 | | | |
| 最小资源 | | | |
| 推荐资源 | | | |
| 网卡 | | | |
版本差异说明:
- •
tcpdump 4.9.3+ 支持更多协议过滤,建议升级 - • Ubuntu 16.04 默认 kernel 4.15 之前,建议升级到 20.04
- • RHEL 7.x 和 8.x 网络栈差异较大(BBR 算法支持度不同)
快速排查流程
- • [ ] 获得 root 权限(tcpdump 需要)
- • [ ]
ping -c 100 target_ip 检查是否丢包 - • [ ]
netstat -s | grep -E "dropped|lost" 看是否有 drop 计数 - • [ ]
ip link show | grep -E "RX|TX" 查看网卡错误包
- • [ ]
tcpdump -i eth0 -w dump.pcap host target_ip 捕获目标流量 - • [ ] 用 wireshark 离线分析 dump.pcap
- • [ ] 逐项排除(系统层 → 应用层 → 网络硬件)
- • [ ] 第四阶段:参数优化与验证(20-60分钟)
诊断流程体系架构
应用层超时 / 丢包告警 │ ▼ ┌─────────────────────┐ │ 症状初步确认 │ │ ping/netstat/ss 检查│ └────────┬────────────┘ │ ┌───────────┼───────────┐ ▼ ▼ ▼ 发送端 网络链路 接收端 诊断法 诊断法 诊断法 ┌──────┐ ┌──────┐ ┌──────┐ │发送 │ │抓包 │ │接收 │ │队列 │ │分析 │ │队列 │ │溢出?│ │丢包?│ │满? │ └──┬───┘ └──┬───┘ └──┬───┘ │ │ │ 通常不是 最常见 绝大多数 │ │ │ └──────────┼───────────┘ │ 根因定位→参数优化
核心判断点:
- 1. 发送端:
netstat -s | grep "Tcp" | grep -i drop - 2. 网络链路:
tcpdump ... | grep -i "RST\|DUP\|RETRAN" - 3. 接收端:
ss -tnis | grep -i recv-q
法则 1:发送端诊断 - 排查队列溢出
目标:检查本机 TCP 发送队列是否溢出,导致数据包未能发出
诊断命令:
RHEL/CentOS:
# 方式 1:查看系统全局 TCP 统计netstat -s | grep -A 20 "Tcp:"# 预期输出示例:# Tcp:# 18452829 active connections openings# 512345 passive connection openings# 75432 failed connection attempts# 8234 connection resets received# 1024567 connections established# 45678234 segments received# 12345678 segments sended out# 4567 segments retransmitted# 0 bad segments received# 12345 resets sent# 关键指标:# - "segments retransmitted":重传次数(越低越好,>1% 表示丢包)# - "resets sent":被迫重置连接数# - "failed connection attempts":连接建立失败
Ubuntu/Debian:
# 同上,完全兼容netstat -s | grep -A 20 "Tcp:"# 若 netstat 不可用,使用 ss(新工具)ss -tnis | head -20
参数解释:
- 1. segments retransmitted:TCP 段重传次数
- 2. snd_buf(发送缓冲区):查看单连接的发送队列
# 查看所有 TCP 连接的发送缓冲使用量ss -tnis | awk '{print $2}' | sort -rn | head -10# 若 Recv-Q 和 Send-Q 都很大,说明队列堆积 - 3. net.core.netdev_max_backlog:网络设备队列最大深度
执行前验证:
# 确认网络接口正常运行ip link show | grep -A 2 "eth0"# 预期输出:# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...# 确认有网络连接ss -tnis | grep -E "ESTAB|LISTEN" | wc -l# 预期输出:数字 > 0
执行后验证 - 查看具体连接队列:
# 查看所有 TCP 连接的 Recv-Q 和 Send-Q(关键指标)ss -tnis | head -20# 预期输出示例:# State Recv-Q Send-Q Local Address:Port Peer Address:Port# ESTAB 0 0 192.168.1.100:46789 192.168.1.101:3306# ESTAB 1024 0 192.168.1.100:46790 192.168.1.102:3306## 说明:# - Send-Q = 0:发送缓冲正常,没有积压# - Send-Q > 0:发送缓冲积压,可能是接收端处理慢或丢包# 统计 Send-Q 不为 0 的连接ss -tnis | awk '$3 > 0 {print}' | wc -l# 若输出 > 0,说明确实有积压常见错误与解决:
❌ 错误 1:看不到 netstat 输出(命令不存在)
# 症状-bash: netstat: command not found# 解决方案 1:安装 net-toolsyum install -y net-tools # RHEL/CentOSapt install -y net-tools # Ubuntu/Debian# 解决方案 2:使用 ss 替代(更新的工具)ss -tnis # 功能等同于 netstat -tnis
❌ 错误 2:netstat 输出看不懂,数字太大
# 原始输出# segments sended out 98765432# 解决方案:用易读的方式展示netstat -s | grep "segments sended out" | awk '{printf "发送总数:%d 万\n", $4/10000}'❌ 错误 3:LISTEN 队列溢出(SYN backlog 满)
# 症状:新连接无法建立# 诊断:查看 Listen 队列状态ss -tnis | grep LISTEN# 预期输出:# State Recv-Q Send-Q Local Address:Port Peer Address:Port# LISTEN 0 128 0.0.0.0:3306 0.0.0.0:*## 说明:Recv-Q 是当前连接数,Send-Q 是最大监听队列(128 可能太小)# 解决方案:增大 backlog# 编辑 /etc/sysctl.confecho"net.core.somaxconn = 65535" >> /etc/sysctl.confsysctl -p
幂等性保障:
- •
netstat -s 仅读取统计信息,不会修改系统状态
回滚要点:
- • 发送队列溢出通常需要从应用层解决(减少请求频率或增加连接池)
法则 2:网络链路诊断 - 抓包精准定位丢包点
目标:用 tcpdump 捕获网络流量,分析是否存在丢包、重传、顺序错乱等迹象
诊断命令:
RHEL/CentOS:
# 先安装 tcpdump(通常已预装)which tcpdump || yum install -y tcpdump# 方式 1:实时抓包并显示(边抓边看)tcpdump -i eth0 -n -vvv -c 1000 \'tcp port 3306 and (tcp[tcpflags] & tcp-syn != 0 or tcp[tcpflags] & tcp-rst != 0)'# 方式 2:抓包到文件,后期用 wireshark 分析tcpdump -i eth0 -w tcp_dump.pcap -B 32000 \'tcp and (host 192.168.1.100 or host 192.168.1.101)'# 说明:# -i eth0:监听 eth0 接口# -n:不做 DNS 反解(加快速度)# -vvv:详细输出# -c 1000:抓 1000 个包后停止# -w:写入文件# -B 32000:缓冲池大小(防止丢包)
Ubuntu/Debian:
# 同上,完全兼容apt install -y tcpdumptcpdump -i eth0 -w tcp_dump.pcap 'tcp and host 192.168.1.100'
参数解释:
- 1. tcp[tcpflags]:TCP 标志位过滤
- 2. -B 缓冲池大小:防止高速网络下抓包丢包
tcpdump -i eth0 -B 32000 ... # 32MB 缓冲
- •
tcp port 3306:只看 MySQL 端口 - •
tcp and (src 192.168.1.100 or dst 192.168.1.100):只看往来某 IP
执行前验证:
# 确认 tcpdump 有足够权限tcpdump -h > /dev/null# 若无权限,用 sudo# 确认网络接口存在ip link show | grep "eth0"# 若 eth0 不存在,用 `ip link show` 查看实际接口名(可能是 ens0)
执行后验证 - 离线分析 pcap 文件:
# 第 1 步:用 tcpdump 过滤关键信息tcpdump -r tcp_dump.pcap -n | grep -E "RST|DUP|retransmission" | head -20# 预期输出:# 20:50:23.456789 IP 192.168.1.100.46789 > 192.168.1.101.3306: Flags [RST], seq 1000, ack 2000# 20:50:24.123456 IP 192.168.1.100.46790 > 192.168.1.101.3306: Flags [.], seq 3000, ack 4000, [tcp sum ok], length 1024## 若发现大量 RST 或 DUP 标记,说明确实有丢包# 第 2 步:统计丢包数量tcpdump -r tcp_dump.pcap | wc -l # 总包数tcpdump -r tcp_dump.pcap | grep "RST\|DUP" | wc -l # 异常包数# 第 3 步:导出为文本便于分析tcpdump -r tcp_dump.pcap -n > tcp_analysis.txt# 然后用 grep、awk 分析# 第 4 步:用 Wireshark GUI 分析(可视化)# 在本地打开 tcp_dump.pcap(通过 scp 下载到本地)scp user@remote:/root/tcp_dump.pcap ./# 在本地用 Wireshark 打开wireshark ./tcp_dump.pcap
Wireshark 分析关键指标:
在 Wireshark 中:1. 菜单 → Statistics → TCP Stream Graph → Round Trip Time - 显示延迟曲线,突增说明有丢包2. 菜单 → Analyze → Expert Info - 自动找出所有警告(如 Retransmission、Duplicate ACK)3. 菜单 → Statistics → Summary - 显示总包数、平均包大小、总流量
常见错误与解决:
❌ 错误 1:tcpdump 权限不足
# 症状-bash: tcpdump: command not found或(Couldn't open device eth0: Permission denied)# 解决方案 1:用 sudosudo tcpdump -i eth0 ...# 解决方案 2:赋予普通用户权限(开发环境)sudo setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump
❌ 错误 2:抓到的包太少或没有目标流量
# 症状:抓了 100 个包,都不是目标 IP 的流量# 可能原因:# 1. 过滤条件写错了# 2. 目标 IP 地址错误# 3. 监听的网卡不对# 诊断:tcpdump -i eth0 -n -c 20 # 不带过滤,看有什么流量# 若没看到目标 IP,说明:# - IP 地址错了# - 或在另一个网卡上# ip addr show # 查看所有网卡和 IP
❌ 错误 3:抓包文件太大,Wireshark 打不开
# 症状:tcp_dump.pcap 文件 > 1GB,打开很慢# 解决方案 1:用 tcpdump 过滤特定时间段tcpdump -i eth0 -w tcp_dump.pcap -G 60 'tcp and host 192.168.1.100'# -G 60:每 60 秒切换一个文件# 解决方案 2:在命令行用 tcpdump 过滤后再导出tcpdump -r large_dump.pcap 'tcp.port == 3306' -w small_dump.pcap
幂等性保障:
回滚要点:
- • 抓包本身无需回滚,但后续根据抓包结果调整参数需要备份
法则 3:接收端诊断 - 排查缓冲区和应用处理慢
目标:检查接收端(Recv-Q)是否堆积,或应用层处理速度跟不上,导致数据丢弃
诊断命令:
RHEL/CentOS:
# 方式 1:查看接收缓冲队列(Recv-Q)ss -tnis | grep -E "ESTAB|State"# 预期输出示例:# State Recv-Q Send-Q Local Address:Port Peer Address:Port# ESTAB 0 0 192.168.1.100:3306 192.168.1.101:46789# ESTAB 512 0 192.168.1.100:3306 192.168.1.102:46790## 若 Recv-Q > 0,说明接收缓冲有堆积# 方式 2:查看套接字缓冲区水位cat /proc/net/tcp | awk '{print $2, $3}' | grep -v "local_address" | awk -F: '{ print $2 ":" $3, $4}' | sort | uniq -c | sort -rn | head -20# 方式 3:系统级接收队列统计netstat -s | grep -i "receive\|drop"# 预期输出示例:# 123456 packets received# 45678 packets dropped# 9876 received on raw socketUbuntu/Debian:
# 同上,完全兼容ss -tnisnetstat -s | grep -i "receive\|drop"
参数解释:
- 1. Recv-Q:TCP 接收缓冲的已收但未读数据字节数
- 2. 系统级丢包:
/proc/net/dev 中的 RX_drop 和 RX_fifocat /proc/net/dev | grep eth0# 输出格式:# eth0: RX bytes RX packets RX errors RX dropped RX overrun RX frame# TX bytes TX packets TX errors TX dropped TX carrier TX collisns
- 3. 应用缓冲设置:应用的 socket 接收缓冲大小
# 查看当前 socket 的接收缓冲cat /proc/sys/net/core/rmem_defaultcat /proc/sys/net/core/rmem_max# 通常默认 128K,高并发需要增大
执行前验证:
# 确认接收端运行正常curl http://192.168.1.100:8080/health# 预期输出:200 OK 或类似成功响应
执行后验证 - 如果发现接收队列堆积,调优:
第 1 步:增大接收缓冲
# 临时调整(立即生效,重启后失效)sysctl -w net.core.rmem_default=134217728 # 128MBsysctl -w net.core.rmem_max=134217728# 验证cat /proc/sys/net/core/rmem_default# 输出:134217728# 永久调整(编辑 /etc/sysctl.conf)cat >> /etc/sysctl.conf << 'EOF'net.core.rmem_default = 134217728net.core.rmem_max = 134217728EOFsysctl -p
第 2 步:增大网络接口的接收队列深度
# 查看当前设置ethtool -g eth0# 预期输出:# Ring parameters for eth0:# Pre-set maximums:# RX: 4096# RX Mini: 0# RX Jumbo: 0# TX: 1024# Current hardware settings:# RX: 256 <-- 当前值太小# RX Mini: 0# RX Jumbo: 0# TX: 256# 增大接收队列ethtool -G eth0 rx 2048 tx 1024# 验证ethtool -g eth0 | grep "RX\|TX" | grep -v "Pre\|Jumbo\|Mini"# 预期输出:RX 应该改为 2048
第 3 步:排查应用层处理慢的根因
# 查看应用进程的状态ps aux | grep your_app# 检查应用的系统调用延迟strace -p <app_pid> -e recv,read -T# 预期输出:# 20:50:23.456789 recv(45, ...., 65536) = 16384 <0.001234>## 若 recv 延迟 > 10ms,说明应用处理慢# 优化建议:# 1. 增加应用线程/进程数# 2. 优化应用代码的数据处理逻辑# 3. 增加服务器资源(CPU/内存)
常见错误与解决:
❌ 错误 1:ethtool 命令不存在
# 症状-bash: ethtool: command not found# 解决方案yum install -y ethtool # RHEL/CentOSapt install -y ethtool # Ubuntu/Debian
❌ 错误 2:增大接收缓冲后反而延迟增高
# 可能原因:缓冲太大导致内存竞争,GC 增加# 解决方案:折中方案,改为 64MB 而非 128MBsysctl -w net.core.rmem_max=67108864# 同时监控内存使用free -h# 若内存持续上升,说明缓冲设置太大
❌ 错误 3:ethtool -G 失败(驱动不支持)
# 症状Cannot get device ring settings: Operation not supported# 解决方案:这是网卡驱动限制,无法解决# 检查网卡驱动是否过旧ethtool -i eth0# 若驱动支持,升级驱动:# 一般需要升级网卡固件和驱动(厂商支持)
幂等性保障:
- • ethtool 调整仅对当前网卡生效,重启后还原(需写入配置文件永久化)
回滚要点:
最小必要原理
TCP 丢包的网络栈全路径
应用层(accept/recv) ↑ │ 应用缓冲队列(Socket Receive Buffer) │ 若满则丢包 │内核态(TCP/IP Stack) ↑ ┌────┴─────────────────────┐ │ TCP 处理(去重、排序、确认)│ │ - 去重复的数据包 │ │ - 排序乱序包 │ │ - 发送 ACK │ │ 若处理不及时,硬件队列满→丢包 │ ├────iperf-backlog 队列────┐ │ 接收队列满→丢包 │ │ ├─网卡驱动(RX ring)────┐ │ 若网卡缓冲溢出→丢包 │ │ └─────网络物理层──────┘
三个关键决策点
- •
tcpdump 看是否有丢包迹象(RST、DUP、超时)
- • 若 Recv-Q > 缓冲的 20%,说明应用处理慢
常见丢包原因的信号
| | |
|---|
| | netstat -s | grep retransmit |
| | ss -tnis | grep "ESTAB" |
| | cat /proc/net/dev |
| | tcpdump ... | grep RST |
| | |
可观测性:监控 + 告警 + 性能基准
Linux 原生监控
# 实时监控 TCP 丢包关键指标watch -n 1 'netstat -s | grep -E "segments|dropped|retrans|reset"'# 预期输出(每秒刷新一次):# 123456 segments received# 45678 segments sended out# 234 segments retransmitted# 12 connection resets received# 34 resets sent
Prometheus 监控规则
# 保存为 tcp_packet_loss_rules.ymlgroups:-name:tcp_packet_lossinterval:30srules:# 重传率过高告警-alert:TCPHighRetransmitRateexpr:| rate(node_tcp_segments_retransmitted_total[5m]) / rate(node_tcp_segments_total[5m]) > 0.01for:5mlabels:severity:warningannotations:summary:"TCP 重传率过高,超过 1%({{ $labels.instance }})"description:"重传率:{{ $value | humanizePercentage }}"# 接收缓冲堆积告警-alert:TCPReceiveQueueBacklogexpr:| node_sockstat_TCP_inuse / node_sockstat_TCP_alloc > 0.8for:5mlabels:severity:criticalannotations:summary:"TCP 接收队列堆积率超过 80%"# 连接重置告警-alert:TCPConnectionResetsexpr:rate(node_tcp_connections_resets_total[5m])>10for:5mlabels:severity:warningannotations:summary:"TCP 连接重置频率过高({{ $labels.instance }})"description:"每秒重置数:{{ $value }}"# 网卡丢包告警-alert:NetworkInterfacePacketLossexpr:| rate(node_network_receive_drop_total[5m]) > 100 or rate(node_network_transmit_drop_total[5m]) > 100for:5mlabels:severity:criticalannotations:summary:"网卡每秒丢包超过 100({{ $labels.device }})"性能基准测试
场景 1:模拟正常网络传输
# 在接收端运行服务器nc -l -p 5000 > /dev/null # nc 监听 5000 端口# 在发送端运行 iperf3 客户端(模拟大流量)iperf3 -c 192.168.1.100 -p 5000 -t 60 -i 10 -R# 预期输出(发送端视角):# [ 10] 0.00-10.00 sec 125 GBytes 1.07 Gbps 0 datagrams# [ 10] 10.00-20.00 sec 125 GBytes 1.07 Gbps 0 datagrams## 关键指标:# - datagrams 为 0:没有丢包# - Gbps 稳定:网络链路正常
场景 2:在 CPU 高负载下测试丢包
# 在接收端启动 CPU 密集任务stress --cpu 8 --timeout 120s &# 同时运行 iperf3 传输iperf3 -c 192.168.1.100 -t 60 -i 10 -R# 对比 CPU 空闲 vs 高负载下的丢包率# 若高负载下丢包率 > 低负载 10 倍,说明 CPU 不足
场景 3:长期稳定性测试(24+ 小时)
# 脚本:持续监控丢包指标并告警#!/bin/bashwhiletrue; do# 每 60 秒采样一次 RETRANS=$(netstat -s 2>/dev/null | grep "segments retransmitted" | awk '{print $1}') DROPS=$(cat /proc/net/dev | grep eth0 | awk '{print $4}')echo"$(date) - 重传:$RETRANS,网卡丢包:$DROPS" >> /tmp/stability_test.log# 若丢包突增(对比前一次),告警if [ $DROPS -gt 1000 ]; thenecho"告警:网卡丢包数异常:$DROPS" | mail -s "丢包告警" admin@company.comfisleep 60done
总结:TCP 丢包 10 大根因
| | | |
|---|
| | | 增大接收缓冲:sysctl -w net.core.rmem_max=134217728(128MB)+ 优化应用处理速度 |
| | /proc/net/dev 中 RX_drop 增加 | ethtool -G eth0 rx 2048 tx 1024 |
| | | 启用 SYN cookie:sysctl -w net.ipv4.tcp_syncookies=1 |
| | | |
| | | iptables -L -v |
| | | echo "net.core.somaxconn=65535" >> /etc/sysctl.conf && sysctl -p |
| | | |
| | | sysctl -w net.ipv4.tcp_retries2=15 |
| | | 统一网卡 / 交换机 MTU 为 1500:ip link set eth0 mtu 1500 |
| | | |