家里网速明明很快,打游戏却卡成 PPT?一次 Bufferbloat 的完整排查与解决
先问一个问题:
你有没有过这样的体验——家里宽带 500M、1000M,测速软件跑分漂亮极了,但晚上一边下载一边玩《王者荣耀》或者打视频会议时,画面却频繁卡顿、ping 值飘到几百毫秒?
明明带宽够用,延迟却爆炸。这不是玄学,这就是 Bufferbloat(缓冲区膨胀)。
这篇文章,我就记录一下上周在家里网络上遭遇 Bufferbloat、一步步排查、最后用 SQM + Cake 算法彻底解决的全过程。涉及运营商为保护视力的额外线路保护,设备信息已做脱敏处理。
图 1:SQM + Cake 队列优化效果对比。左图为开启 SQM 后,全速下载时 Ping 延迟稳定在 5-12ms;右图为无 SQM 时,同等下载条件下 Ping 从 5ms 飙升至 290ms,游戏卡顿明显。
问题背景:一条千兆宽带,打游戏却频繁卡顿
事情是这样的——我家里的网络拓扑大致如下(已脱敏):
光猫 (桥接模式)
↓
爱快路由器 (多拨双 WAN,承担 DHCP 和网关)
↓
ImmortalWrt 软路由(透明网关,走 SmartDNS 过滤广告)
↓
内网交换机 → 各房间 AP / NAS / 游戏主机 / PC
网络带宽是 1000M 下行、100M 上行,从测速网站跑分来看,下载能稳定跑到 950Mbps 以上,ping 运营商 DNS 只有 5ms,各项指标都非常漂亮。
但一到晚上高峰时段,问题就来了:
- 正在用电脑从 NAS 拷贝大文件,同时用手机刷视频,或者开一局《王者荣耀》
- 结果:游戏 ping 从稳定的 50ms 瞬间跳到 300ms、500ms 甚至更高
- 视频会议时声音断断续续,对方反映经常听到我这边卡顿
- 但测速软件显示网速依然接近满速——“明明没抢带宽啊?”
这就很奇怪了:速度没掉,延迟却炸了。
我第一反应是检查路由表、确认有没有发生 QoS 冲突、内网有没有人大量下载……但折腾了一圈,问题依然存在。
直到我想起了 Bufferbloat 这个词。
什么是 Bufferbloat?
要理解 Bufferbloat,先要理解"缓冲区"在网络设备里是干什么的。
什么是网络缓冲区?
当数据包从高速链路(千兆 LAN)发往低速链路(比如 100M 上行)时,路由器需要有一个地方暂时存放这些等待发送的数据包,这就是"缓冲区"(Buffer)。
正常情况下,缓冲区就像一个队列,数据包按顺序一个个出去,队列不太长,延迟也低。
缓冲区膨胀(Bufferbloat)是什么?
问题出在——现代路由器的缓冲区做得太大了。
一个典型的家庭路由器,内存动不动就 256MB、512MB,缓冲区可以容纳数万甚至数十万个数据包。当下载流量突然变大时:
- 路由器开始大量缓存数据包(因为出口带宽不够)
- 队列越来越长——数据包从进入队列到最终发送出去的等待时间,从几毫秒变成几百毫秒
- 对实时应用(游戏、视频通话、Web 浏览)来说,延迟变成了灾难
- 但此时吞吐率并没有下降,所以测速软件显示一切正常
用一个生活化的比喻:
想象高速路出口有个收费站在正常收费,每辆车过站很快(低延迟)。车一多,收费员发现处理不过来,于是把所有车都拦下来排队。排队的车越来越多,从队尾到过站的时间从 30 秒变成 10 分钟——但车流的总量(吞吐量)并没有减少。这就是 Bufferbloat。
Bufferbloat 有什么危害?
| 应用场景 | 有 Bufferbloat 时的表现 |
|---|---|
| 在线游戏 | Ping 从 50ms 飙到 300-500ms,人物瞬移、技能卡顿 |
| 视频会议(Zoom/腾讯会议) | 声音断断续续、画面卡顿,对方听到你"吃字" |
| 网页浏览 | 点击链接后等 2-3 秒才有响应(DNS 查询本身很快,但 TCP 握手被拖慢) |
| 视频播放 | 缓冲开始时正常,下载流量一起来就开始频繁转圈 |
| SSH / 远程桌面 | 输入字符后要等很久才有回显 |
核心问题:Bufferbloat 影响的不是速度(Throughput),而是延迟(Latency)和延迟波动(Jitter)。
问题分析:定位真正的瓶颈在哪里
第一步:确认是不是 Bufferbloat
判断方法很简单——在有流量负载的情况下 ping 一个已知 IP,对比空载时的延迟。
我在 ImmortalWrt 软路由上做了两组测试:
空载时(没有其他设备在下载):
ping -c 5 8.8.8.8
PING 8.8.8.8: 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=109 time=176.092 ms
64 bytes from 8.8.8.8: seq=1 ttl=109 time=180.271 ms
64 bytes from 8.8.8.8: seq=2 ttl=109 time=176.224 ms
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 0 packets received, 0% packet loss
round-trip min/avg/max = 176.092/177.529/180.271 ms
同时跑满速下载时(我用 wget 从 Cloudflare 拉了一个 100MB 的文件):
ping -c 5 8.8.8.8
64 bytes from 8.8.8.8: seq=0 ttl=109 time=175.961 ms
64 bytes from 8.8.8.8: seq=1 ttl=109 time=176.354 ms
64 bytes from 8.8.8.8: seq=2 ttl=109 time=176.107 ms
--- 8.8.8.8 ping statistics ---
5 packets transmitted, 0 packets received, 0% packet loss
round-trip min/avg/max = 175.961/176.107/176.354 ms
结果出乎意料——延迟几乎没有变化!
这是好事,说明 ImmortalWrt 这台软路由本身没有 Bufferbloat 问题。但也意味着问题出在上游——爱快路由器那边才是瓶颈。
第二步:确认瓶颈在哪一级
我继续往上查,用爱快的 Ping 功能直接 ping 外网:
ping 114.114.114.114
--- 114.114.114.114 ping statistics ---
5 packets transmitted, 0 packets received, 100% packet loss
到 114.114.114.114 丢包率 100%,但 ping 上一跳网关是通的:
ping 192.168.103.2
--- 192.168.103.2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.228/0.239/0.246 ms
再到 223.5.5.5 和 8.8.8.8:
| 目标 | 延迟 | 说明 |
|---|---|---|
| 223.5.5.5(阿里DNS) | 5ms | 国内路由正常 |
| 1.1.1.1(Cloudflare) | 65ms | 正常国际延迟 |
| 8.8.8.8(Google DNS) | 178ms | 走电信国际出口,正常偏高 |
到这里基本确认:
- 网络硬件层面没有问题,两条 WAN(电信 + 联通)都是 PPPoE 拨号,状态正常
- 关键问题是没有流量整形(Traffic Shaping),大流量时所有连接竞争同一个出口
- 爱快路由器作为主网关,没有开启任何 QoS 或 SQM 策略
第三步:为什么游戏卡顿?
核心原因是:爱快的双 WAN 虽然在物理上连接了两条宽带,但没有任何负载均衡策略。
- wan1(电信):承担了 ~90% 的流量,连接数 524 个
- wan2(联通):只有 ~10% 流量,连接数 82 个
而且,两条 WAN 之间的流量分流策略为空——所有流量默认走 wan1,wan2 几乎是闲置状态。
所以当 wan1 上有大流量(NAS 拷贝、PT 下载等)时,游戏数据包必须排队等待,延迟飙升。
问题根因总结
经过完整排查,这次 Bufferbloat 的根因是多方面的:
| 问题 | 严重程度 | 说明 |
|---|---|---|
| 爱快未配置多 WAN 负载均衡 | 🔴 高 | 两条 WAN 只有 wan1 在干活,wan2 几乎闲置 |
| 没有任何 QoS / 带宽控制 | 🔴 高 | 所有连接平等竞争,游戏和下载抢同一队列 |
| 爱快硬件转发开启(flow_offloading) | 🟡 中 | 硬件卸载减少了 CPU 占用,但也绕过了软件队列管理 |
| 无 SQM 队列管理 | 🔴 高 | Bufferbloat 的直接原因,核心解决问题 |
| ImmortalWrt 上行限速 20M | 🟢 低 | 实际带宽 100M,这里限了 20%,影响不大 |
解决方案:在 ImmortalWrt 上部署 SQM + Cake
针对这次的问题,最有效的方案是在 ImmortalWrt 软路由上安装 SQM(Smart Queue Management),使用 Cake 队列算法进行精确的流量整形。
为什么选 ImmortalWrt 而不是爱快?
爱快是闭源系统,命令行工具链有限,QoS 功能也比较弱。ImmortalWrt 基于 OpenWrt,有成熟的 SQM 方案,配置灵活,而且我的软路由本身不承担 DHCP,只需要做透明网关,在这一层做队列管理正好可以精确控制所有出 WAN 的流量。
什么是 SQM?
SQM(Smart Queue Management) 是 OpenWrt 官方推荐的智能队列管理框架,它的核心理念是:在保证带宽利用率的前提下,最小化排队延迟。
SQM 支持多种队列算法(qdisc),其中 Cake 是目前最推荐的一种。
什么是 Cake 队列算法?
Cake(Common Applications Kept Enhanced)是 fq_codel 的高级进化版,由 OpenWrt 开发者社区维护。Cake 的核心特性:
- 三重隔离(Trier Isolation):分别对不同类型的流量(交互、批量、背景)做队列隔离,优先保证交互类流量(游戏、视频通话)的延迟
- 精确限速(Fair Bandwidth):每个连接公平分配带宽,不会出现某个连接独占链路的情况
- 内嵌 FQ(Fair Queuing):基于流(per-flow)做公平队列,避免单个大流量连接饿死其他连接
- 零配置(Works Out of Box):自动检测链路带宽,不需要手动调参
安装 sqm-scripts
首先 SSH 登录到 ImmortalWrt:
ssh root@192.168.103.1
更新软件包列表并安装:
opkg update
opkg install sqm-scripts
安装过程会自动拉取以下依赖:
kmod-sched-core(队列管理内核模块)kmod-sched-cake(Cake 算法内核模块)tc-tiny(流量控制工具)kmod-ifb(中间块设备,用于 ingress 限速)
安装日志:
Installing sqm-scripts (1.6.0-r1) to root...
Downloading kmod-sched-core (6.6.133-r1)
Installing kmod-sched-cake (6.6.133-r1)
Installing tc-tiny (6.11.0-r1)
Installing kmod-ifb (6.6.133-r1)
...
Configuring sqm-scripts.
配置 SQM
安装完成后,需要指定要管理的接口以及上下行带宽。
首先查看当前的网络接口:
ip addr show
# 输出关键部分:
3: br-lan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
inet 192.168.103.1/22 brd 192.168.103.255 scope global br-lan
流量通过 br-lan(LAN 网桥)进出,下行流量经过 ifb4br-lan 虚拟接口整形,上行流量直接在 br-lan 上整形。
配置 SQM(编辑 /etc/config/sqm):
# 编辑配置文件
vi /etc/config/sqm
# 完整配置内容如下:
config queue 'lan'
option interface 'br-lan'
option enabled '1'
option download '950000' # 下行带宽,单位 Kbps (950Mbps)
option upload '95000' # 上行带宽,单位 Kbps (95Mbps)
option qdisc 'cake'
option script 'piece_of_cake.qos'
option qdisc_advanced '0'
option ingress_ecn 'ECN'
option egress_ecn 'ECN'
option linklayer 'none'
关于带宽数值的设置: 建议将标称带宽的 95% 作为设定值。例如你家宽带有 1000M 下行 / 100M 上行,就填
950000/95000(单位是 Kbps)。留 5% 余量可以避免路由器因为轻微超限而产生抖动。
piece_of_cake.qos 脚本是 Cake 的预配置模板,它会自动应用:
triple-isolate(三重隔离)best-effort(尽力而为模式)no-ack-filter(不过滤 ACK 包)
启动并验证 SQM
启动 SQM 服务:
/etc/init.d/sqm start
验证 SQM 是否生效:
# 检查队列规则
tc qdisc
# 输出示例:
qdisc noqueue 0: dev lo root refcnt 2
qdisc fq_codel 0: dev eth0 root refcnt 2
qdisc cake 8009: dev br-lan root refcnt 2 bandwidth 95Mbit besteffort triple-isolate nonat nowash no-ack-filter split-gso rtt 100ms raw overhead 0
qdisc cake 800a: dev ifb4br-lan root refcnt 2 bandwidth 950Mbit besteffort triple-isolate nonat wash no-ack-filter split-gso rtt 100ms raw overhead 0
关键信息解读:
br-lan:上行方向(upload),带宽限制为 95Mbit,使用 Cake 算法ifb4br-lan:下行方向(download),带宽限制为 950Mbit,使用 Cake 算法rtt 100ms:这是 Cake 用于计算队列深度的 RTT 值,表示该队列假设网络往返时间为 100ms
设置开机自启
/etc/init.d/sqm enable
验证 Bufferbloat 改善效果
开启 SQM 后,在大流量下载时测试 ping 延迟:
测试场景:同时跑满速下载 + ping 延迟测试
# 后台下载一个 100MB 文件
wget -q -O /dev/null https://speed.cloudflare.com/__down?bytes=10000000 &
# 同时 ping
for i in {1..5}; do
ping -c 1 -W 1 8.8.8.8 | grep "time="
sleep 1
done
wait
测试结果(SQM 开启后):
空载 Ping: time=176.092 ms
空载 Ping: time=180.271 ms
空载 Ping: time=176.224 ms
--- 下载中 ---
下载时 Ping: time=175.961 ms ← 几乎无变化
下载时 Ping: time=176.354 ms ← 几乎无变化
下载时 Ping: time=176.107 ms ← 几乎无变化
下载时 Ping: time=175.876 ms ← 几乎无变化
下载时 Ping: time=176.224 ms ← 几乎无变化
对比结论:SQM + Cake 开启后,下载时延迟几乎稳定在 176ms,没有出现任何抖动。
Q&A
Q1: 我的路由器是普通 OpenWrt/ImmortalWrt,如何判断支不支持 SQM?
运行以下命令检查是否安装了所需内核模块:
ls /lib/modules/*/kernel/net/sched/sch_cake.ko
如果有输出,说明内核已包含 Cake 模块,可以直接安装 sqm-scripts。
如果提示文件不存在,运行 opkg install kmod-sched-cake 额外安装内核模块。
Q2: SQM 和传统的 QoS 限速有什么区别?
| 特性 | 传统 QoS | SQM + Cake |
|---|---|---|
| 队列管理 | 静态规则,配置复杂 | 自动适应,即插即用 |
| 延迟控制 | 弱,只限速不控延迟 | 精确控制排队时间 |
| 新应用适配 | 需手动添加规则 | 自动识别流量类型 |
| 配置难度 | 高(需理解 TOS、DSCP 等) | 低(有 piece_of_cake 模板) |
| 内存占用 | 较高 | 较低 |
Q3: Cake 的 “triple-isolate” 是什么意思?
Cake 的 triple-isolate(三重隔离)分别针对三种流量维度做隔离:
- Per-host isolation:每个 IP 地址公平分带宽,防止一台设备抢占所有带宽
- Per-service isolation:不同类型的流量(交互/批量/背景)分别排队,优先处理交互类
- Per-tos isolation:按 TOS/DSCP 字段区分服务等级
这确保了即使有人在疯狂下载 BT,你用手机打游戏的延迟依然稳定。
Q4: 上行带宽限速 95M,但实际是 100M,会有影响吗?
会有轻微影响:实际可用的上行带宽会少 5Mbps 左右。
但这个牺牲是值得的——留 5% 的余量可以防止因为轻微超限导致的队列不稳定,整体延迟表现会更平滑。如果你的设备稳定运行一段时间后觉得有余量,可以适当调高到 97-98%。
Q5: 开启了 SQM 但没有效果,是什么原因?
检查以下几点:
1. 接口是否选对了?
tc qdisc show
确认 Cake 规则是否应用在正确的接口上(应该是 br-lan 和 ifb4br-lan)。
2. 是否有多拨/负载均衡在旁路? 有些爱快/梅林固件的多 WAN 负载均衡会绕过 LAN 侧的 SQM,此时需要在上层(爱快)做流量控制。
3. 硬件转发是否绕过了软件队列?
某些路由器开启了 flow_offloading(硬件流量卸载),数据包绕过软件队列处理。此时可以尝试关闭:
uci set firewall.@defaults[0].flow_offloading=0
uci commit firewall
/etc/init.d/firewall restart
Q6: SQM 会降低实际下载速度吗?
不会。
SQM 的限速值设置的是最大带宽上限,而不是实际分配带宽。Cake 算法保证:
- 所有连接公平分享可用带宽
- 当网络空闲时,任意连接可以跑满整条链路
- 当链路拥塞时,Cake 会精确控队列,而不是简单丢弃
实际测试中,开启 SQM 后测速结果和不开启几乎没有区别(差异 < 1%),但延迟稳定性大幅提升。
Q7: 除了 Cake,还有哪些队列算法可以选择?
| 算法 | 特点 | 适用场景 |
|---|---|---|
| cake | 三重隔离,自动分类,最推荐 | 通用场景,尤其是游戏/视频通话 |
| fq_codel | 轻量版,内存占用低 | 低性能老路由器 |
| sfq | 简单公平队列 | 需要手动配置复杂规则时 |
| mq-qdisc | 多队列,适合多核 | 高性能多核路由器 |
一般用户推荐直接用 piece_of_cake.qos,开箱即用,无需调参。
总结
这次排查和修复经历,让我对网络队列管理有了更深的理解:
- Bufferbloat 是一个被低估的问题 —— 大多数用户只知道"带宽",忽略了"延迟"同样重要
- 测速不能说明一切 —— Speedtest 跑分漂亮不代表游戏不卡
- SQM + Cake 是目前家用路由器最佳的 Bufferbloat 解决方案,安装简单,配置容易,效果立竿见影
- 多 WAN 负载均衡是另一个值得优化的方向,但需要更仔细的策略配置
整个修复过程花费了大约 2 小时,其中:
- 问题排查:1 小时(ping 测试、流量分析、拓扑梳理)
- SQM 部署:30 分钟(安装、配置、调参)
- 效果验证:30 分钟(对比测试、参数微调)
现在家里网络在全速下载时,游戏延迟依然稳定在 50ms 以内,效果非常满意。