判断网络拥塞需综合ss -i(重传、rwnd)、tc qdisc(backlog持续>0)、/proc/net/snmp(tcpretranssegs陡增)等指标;ping丢包率比延迟更敏感;调优需匹配算法(如bbr+fq)、关闭干扰offload,并协同端到端队列控制。

怎么看当前有没有网络拥塞
Linux 本身不直接报“拥塞”这种语义错误,得看底层指标是否异常。最直接的信号是 ss -i 或 netstat -s 里大量重传、SACK 丢弃、接收窗口收缩;tc qdisc show 显示队列已满(backlog 持续 >0);cat /proc/net/snmp 中 TcpRetransSegs 短时间陡增。
-
ss -i能看到每个连接的重传数(retrans字段)、RTT 估算(rtt)、接收窗口(rwnd)。如果某连接retrans> 0 且rwnd接近 0,基本就是接收方被压垮了 -
tc qdisc show dev eth0如果显示backlog 12345b且持续不降,说明内核发送队列在排队,不是应用慢,是链路或中间设备卡住了 - 不要只盯
ping延迟:拥塞时 RTT 可能只涨几十毫秒,但丢包率和重传会明显上升;ping -c 10 -q google.com后看丢包率比看平均延迟更有效
为什么改 net.ipv4.tcp_congestion_control 有时没用
换算法(比如从 cubic 切到 bbr)不等于立刻改善,它只影响发端行为,前提是路径支持、接收方不拖后腿、且你真在受控链路上。
-
bbr需要内核 ≥ 4.9,且必须加载tcp_bbr模块:modprobe tcp_bbr && echo "tcp_bbr" > /etc/modules;否则sysctl -w net.ipv4.tcp_congestion_control=bbr会静默失败 -
bbr在纯丢包场景(如 Wi-Fi 干扰)下反而不如cubic,因为它靠测量带宽而非丢包触发降速;若你实际问题是中间路由器缓存溢出(bufferbloat),bbr+fqqdisc 才管用 - 改完记得验证:
sysctl net.ipv4.tcp_congestion_control输出必须是你设的值;有些发行版(如 RHEL 8)默认禁用bbr,需额外打开net.core.default_qdisc=fq
tc qdisc add dev eth0 root fq 为什么比 pfifo_fast 有效
fq(Fair Queueing)不是简单“加个队列”,而是给每个 TCP 流分配独立子队列 + 主动控制排队时长,直接缓解 bufferbloat;而默认的 pfifo_fast 是无差别 FIFO,一个大流就能堵死整个出口。
-
fq默认限制每个流排队不超过 10ms 的数据量(由flow_limit和quantum控制),避免单个长连接占满缓冲区 - 必须配合
bbr或cubic使用才有意义:如果拥塞算法还在狂发,fq只是把丢包从“随机丢”变成“精准丢”,效果有限 - 注意硬件 offload 干扰:某些网卡(如 Intel X550)开启
tx offload时,tc统计可能不准;可临时关掉:ethtool -K eth0 tx off
应用层写法怎么加重拥塞
很多服务端代码看似正常,实则绕过内核拥塞控制,比如手动拼包、禁用 Nagle、或反复 write() 小数据不 flush。
-
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on))开启后,小包立刻发,但若应用层节奏失控(比如每毫秒写 100B),就会制造大量微突发,把中间交换机缓冲区打爆 - 使用
sendfile()或splice()时,如果源文件句柄是普通磁盘文件,内核可能一次读多页再发,造成突发流量;换成read() + write()并控制每次write()大小(如 ≤ 4KB),反而更平滑 - Go 的
http.Server默认启用KeepAlive,但若客户端不按规范发Connection: close,连接长期空闲又突然发大数据,容易触发fq的 burst 惩罚;可调低IdleTimeout缓解
复杂点在于:拥塞从来不是单一模块的问题。你调了 bbr,但上游 CDN 用 cubic;你配了 fq,但物理交换机缓存有 2MB;你关了 TCP_NODELAY,但业务逻辑本身就在高频轮询。真实环境里,得同时看发端、收端、中间设备三处的队列状态。










