274MB文件,传到本机虚拟机,耗时2分钟。不是跨洋传输,不是云端实例,是localhost——同一台机器,回环地址,网络延迟为零。
这是GitHub issue #290的原始反馈。cubic,一个7000行Rust代码写的轻量级虚拟机管理工具,把用户卡在了这个荒诞场景里。维护者复现、追踪、定位,最终发现问题不在自家代码,而在上游依赖russh-sftp——一个被大量Rust项目使用的SFTP库。
更荒诞的是修复方案:放弃SFTP,退回一个1983年设计的协议。
2MB/s天花板:协议层的隐形刹车
SFTP(SSH文件传输协议)的设计初衷是安全,不是速度。它跑在SSH连接之上,每个数据包都要经过加密、序列化、确认。问题出在"请求-响应"模型:客户端发一个读请求,等服务端确认,才能发下一个。
这个模型在慢速网络下没问题。但当带宽延迟积(带宽×往返时间)变大时,管道里永远只存在一个未完成请求,大量带宽被闲置。
russh-sftp的实现加剧了这个问题。它的API设计让批量请求难以实现,开发者被迫串行处理。结果就是:无论你的SSD多快、网卡多猛、CPU多闲,传输速度被锁死在2MB/s左右。
这不是Rust的问题。Python的Paramiko、Node.js的ssh2-sftp-client,同样实现都受限于SFTP协议本身。区别在于,其他生态有成熟的替代方案,而Rust社区一度把russh-sftp当作"够用就行"的默认选择。
cubic的架构:干净,但卡在最后一公里
cubic的代码结构值得细看。src/commands/放CLI子命令,src/actions/放业务逻辑,src/instance/管虚拟机状态,src/image/处理镜像下载,src/ssh_cmd/负责SSH和文件传输。依赖极简:russh管连接,russh-sftp管传输,clap管解析,reqwest管下载。
一个有趣的设计是异步边界处理。内部用tokio和russh做异步,但CLI层完全同步。AsyncCaller结构体包装一个多线程runtime,暴露call()方法阻塞等待future。每个命令创建一个实例,跑完异步逻辑,返回同步结果。
这种"内部异步、外部同步"的模式避免了async传染,让CLI代码保持直观。没有到处乱飞的.await,没有生命周期地狱。
镜像管道同样扎实。从发行版镜像站拉取cloud镜像,校验SHA-256/SHA-512,显示进度条,本地缓存。添加新发行版只需在image_factory.rs的DISTROS静态数组里加一行。Rocky Linux就是这样被加进来的。
唯独SSH层留了技术债。SFTP实现直接委托给russh-sftp,进度条耦合在AsyncTransferView包装器里。这本身没问题,直到性能瓶颈暴露——想换传输协议,得先解耦这一层。
SCP:1983年的老东西,怎么就成了最优解
修复方案是换回SCP(Secure Copy Protocol)。1983年的设计,基于SSH但不走SFTP子系统,用管道直接传输数据流。
SCP没有SFTP的确认开销。它把文件切成块,流水线发送,靠TCP自身的拥塞控制做流量调节。在现代网络环境下,这个简单粗暴的策略反而更高效。
cubic的维护者rogkne在issue里确认了修复:用scp命令替代russh-sftp,同文件传输时间从2分钟降到3秒。不是优化,是数量级的差距。
这个选择有代价。SCP的功能比SFTP少,不支持断点续传、目录列表、权限修改。但对cubic的场景——"把文件拷进虚拟机"——这些功能本就不需要。
更深层的问题是Rust生态的缺口。russh-sftp的问题被报告后,社区讨论集中在"如何改进SFTP实现",而非"什么场景该用SFTP"。直到cubic踩坑,才有人认真评估替代方案。
这不是批评。7000行代码、40个star的项目,能挖出影响整个技术栈的协议层问题,本身就是开源协作的价值。rogkne的日常维护节奏——每天提交、数小时内审PR——让这个问题被快速响应。
性能陷阱的共性:当"够用"变成"默认"
russh-sftp的2MB/s限制,和语言无关,和实现细节有关。它暴露的是一类常见陷阱:开发者选依赖时,优先考虑"功能覆盖"和"API易用",而非"场景匹配"。
SFTP的卖点是功能完整、标准统一。但"标准"不等于"适合"。本地虚拟机传输、内网高速链路、批量小文件——这些场景下,SFTP的交互开销是纯粹的浪费。
cubic的修复不是技术升级,是技术降级。用更老的协议,换更简单的实现,得更好的性能。这种"开倒车"在工程里不丢人,丢人的是为了面子硬撑。
rogkne在issue关闭前的最后一条评论提到,正在考虑把SCP作为默认方案,SFTP作为可选回退。这个决策顺序本身,就是对"默认选择"的重新思考。
274MB,2分钟变3秒。数字背后是一个更普适的问题:你的工具链里,有多少"默认选项"其实从未被质疑过?
热门跟贴