yamux 多路复用把隧道吞吐压到 1/10:一次 frp 性能排查

举报
yd_268799136 发表于 2026/10/23 19:08:40 2026/10/23
【摘要】 同一链路下 frp 单连接吞吐只有 ssh 隧道的十分之一。排查到瓶颈是 yamux 的 256KB 流控窗口,给出计算式、实测对比与关闭 tcpMux 时的顺序坑。

现象

两个节点之间 RTT 约 70ms,用 frp 做内网穿透,单连接下载吞吐只有 1.5 MB/s 左右,而且越跑越慢。同一链路改用 ssh 反向隧道(同样加密、同样走这条跨境路径)能稳定跑到 14.7 MB/s,差距接近 10 倍。

先排除的三件事

  1. 带宽。用不可压缩数据测裸 TCP 上行,15.5 MB/s,链路本身没问题。这一步容易踩坑:第一版基线是用 dd if=/dev/zero 通过 ssh 测的,拿到 20.8 MB/s 的"好数据",但 ssh 压缩把零流几乎消光了,这个基线是假的。换成 /dev/urandom 才看到真实值。
  2. 限速与流控。tc qdisc 是默认的 fq,没有额外限速规则;两端 frp 进程 CPU 都很低。
  3. 并发争抢。用多条并发连接聚合测试,4 条并发能到 8.5 MB/s,说明不是总带宽被限死,而是单条连接被卡住。

定位到 tcpMux

frp 的 transport.tcpMux 默认开启,用 yamux 在一条 TCP 连接上复用多条逻辑流。yamux 给每条流分配独立的流控窗口,默认 256 KB。单连接吞吐上限就是「窗口 ÷ RTT」:

256 KB ÷ 70 ms ≈ 3.6 MB/s

实测只有 1.5 MB/s,比这个理论上限还低,因为多条逻辑流共用同一个窗口,还要等窗口回补的往返。

验证

同一台机、同一条链路、同一个文件、同样 32 MB 的 Range 请求:

传输方式 吞吐
裸 TCP(urandom,不可压缩) 15.5 MB/s
ssh 反向隧道 14.7 MB/s
frp,tcpMux = true 3.4 ~ 9.4 MB/s,越跑越慢
frp,tcpMux = false 10.7 ~ 19.8 MB/s

关掉 mux 后,每条隧道连接独占一个 TCP socket,交给内核的拥塞控制和窗口自动调优,不再受固定流控窗口约束。

改法与两个坑

两侧配置都要改:

# frps.toml 与 frpc.toml
transport.tcpMux = false

顺序不能颠倒。 tcpMux 是协商参数,两侧不一致时 frpc 收到 EOF 会直接退出(loginFailExit 默认 true),不会重试。正确顺序是先改并重启 frps,再启动 frpc。

验证不能只看进程在不在。 不一致的失败模式恰好是「进程活着但隧道不通」,systemctl is-active 和 ps 都看不出来。要实际拉一次数据。

结论

frp 的吞吐瓶颈通常不在带宽,而在 tcpMux 的固定流控窗口。RTT 越高、窗口越小,损失越大。70ms 的跨境链路上,这一项就能造成 3 倍以上的差距。判断是否需要关闭,先算一遍「窗口 ÷ RTT」,再和实际带宽比一比,就知道天花板在哪。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。