Dropbox | 为什么我们要为 Capture 构建自定义 Rust 库

翻译:张汉东

原文: https://dropbox.tech/application/why-we-built-a-custom-rust-library-for-capture


Dropbox Capture 是一个新的视觉交流工具,旨在使团队能够轻松地使用屏幕记录、视频信息、屏幕截图或GIF来异步分享他们的工作。不需要正式的入职培训,你可以在几秒钟内开始分享你的想法。事实上,简单性是Capture体验的关键,而这一价值也延伸到Capture底层代码的开发中。

我们团队的指导原则之一是 "成为一个玛格丽特比萨(Margherita pizza)"。正如玛格丽特披萨的完美之处在于其简单性:除了番茄酱、马苏里拉奶酪和罗勒,你不必需要其他的东西。我们很早就知道,Electron和Node会让我们很容易为macOS和Windows建立一个跨平台的TypeScript应用。但要找到合适的第三种成分,使我们能够快速、简单、可靠地调用原生操作系统级别的代码,需要更多的试验。

理想的情况是,我们想要一个精简的代码库,可以无痛地、一致地针对多个平台,并且便于我们的开发人员构建。我们还希望对屏幕捕捉和录音的能力有更多的控制,更好的错误处理,以及更快的幕后性能。事实上,我们正在寻找一种能在每一层都有更多控制的东西--它不需要跳过那么多圈来调用本地代码--并能更好地支持我们想要建立的新功能。

我们有很多方法可以解决这些问题--也许是TypeScript或C++,但最后我们决定使用Rust。

在某些方面,这是个容易的决定。Dropbox拥有一个蓬勃发展的开发者社区,他们将Rust纳入我们的产品。Rust是我们的桌面客户端最近重写的同步引擎的核心,它使你电脑上的Dropbox文件夹像魔术一样工作。我们还把它用于文件压缩,用于崩溃报告基础设施,以及用于Magic Pocket--超字节规模的定制存储基础设施,以优化文件数据的存储

事实证明,Rust也很适合我们的需求。构建一个定制的Rust库有助于解锁更高质量的屏幕记录,从720p到4K,使屏幕截图和屏幕记录可以更快地分享,并大大改善我们的错误处理能力,使我们能够为用户提供更可靠的体验。

我们还认为这将是一个有趣的借口来学习近年来最受欢迎的编程语言。

译注:Dropbox 使用 Rust 的经验已经有四五年了,为什么还要说找借口来学习 Rust 这句话呢?我想,也许 Capture 这个产品对应部门是第一次使用 Rust。

从黑客周(Hack Week)而来

Capture 开始是一个内部黑客周项目,快速迭代是关键。在早期的版本中,我们使用了一些第三方库来做一些事情,如拍摄屏幕截图和处理GIF。将一些预先存在的代码拼凑在一起,帮助我们快速开发一个原型,测试我们最初的假设,并尝试新的功能。但是,当我们考虑到Capture代码的长期健康状况以及这些第三方库引入的所有复杂性时,我们知道最终将不得不偿还早期的技术债务。

我们使用的第三方库通常是基于shell的应用程序;Capture将向每个shell应用程序发送命令,并接收stderr/stdout响应作为回报。这意味着每次我们想完成某些任务时都要启动一个应用程序,或者在某些情况下,有一个应用程序持续运行并等待输入--这并不完全是理想的。

更重要的是,这也意味着Capture与本地代码的通信方式存在一些固有的脆弱性。来自shell应用程序的每一行输出都必须被解析。如果某一行未能解析,就会被认为这是一个错误,而如果是一个错误,问题很可能被掩盖了,我们不会知道在本机代码中究竟发生了什么故障。正如你所预料的,这使得监测和处理错误变得很困难。

从开发者的角度来看,这些库还带来了其他挑战。我们发现macOS和Windows之间的API可能有很大的不同,甚至在同一个跨平台库中也是如此,这增加了为这两个平台开发的复杂性。有些库维护得很好,但缺少我们需要的功能,而另一些库拥有我们想要的一切,但维护得并不那么好。每一个库都提出了我们必须解决的权衡问题,有些比其他库更容易解决。

例如,如果我们想对单个库进行任何改变,我们必须拥有如何构建每个库的机构知识,然后将它们构建到Capture中。案例:我们的一位工程师花了几个小时的宝贵开发时间来学习如何构建Windows屏幕记录库,只是为了修复一个单一的解析错误。

Rust 来拯救

由于Rust对Capture团队来说是个新事物,最初,我们专注于重写简单的功能,否则需要第三方库。例如,激活窗口(activate-windows)以前是一个仅适用于macOS的库,把一个窗口带到最前面,并且只记录该窗口。我们很快就能把这个功能用 Rust 移植到macOS上,然后再把这个功能带到Windows上,因为它以前并不存在。

这些早期的成功给了我们信心去尝试更多雄心勃勃的东西。我们学得越多,就有越多的功能被转移到我们的自定义Rust库中,这使Capture在很多方面受益。

没有开销。有了Neon绑定,我们现在可以轻松地从TypeScript调用本地操作系统的代码,而没有任何开销(而且也更可靠)。换句话说,我们不再需要为完成某些任务而启动单独的shell应用程序。例如,拍摄屏幕截图,曾经是异步的,需要我们等待来自shell应用程序的响应,现在是立即和快速的。

更好的错误处理。Rust还极大地提高了我们处理错误的能力。一旦Capture的大部分代码在我们自己的库中运行,并且在macOS和Windows中具有一致的API,我们就能够添加更强大的日志和监控。不再试图解释来自shell应用程序的输出! 将所有的代码放在一个地方,让我们更深入地了解我们的应用程序实际上是如何运作的。

更多的控制。对库的所有权意味着可以更快地进行修复和改进。把我们所有的代码放在一个地方也使我们更容易解决一些模糊的问题,比如在捕捉或录制超过三个屏幕时不断遇到的不稳定性。这也导致了跨平台的构建管道更加简单。

一个更小的足迹。不需要包括第三方库也减少了我们应用程序的整体大小。例如,在用Rust重写这些功能后,macOS上不再需要大约17MB的Swift库。现在我们可以根据需要简单地调用函数--而不是一直在后台运行 shell 应用程序--我们需要的内存也比以前少了。

新的功能。正如我们在激活窗口时发现的那样,迁移到Rust也使我们能够做一些以前做不到的事情。我们能够为Windows带来以前只存在于macOS上的功能。我们还能够引入一个新的裁剪工具,新的录音控制,并增加新的录音类型,如音频或相机,以及增加录音质量到720p/1080p/4K。这并不是说我们不能用其他语言来构建这些东西,而是Rust让我们能够比以前更快、更省力地构建这些东西。

Capture 和 Rust的下一步是什么

Capture现在是Beta版,如果你还没有,你应该试试。

在开发Capture时,我们最大的惊喜之一是使用Rust是如此简单。在短短几周内,我们就将Rust代码推向了生产,而且我们从Dropbox精通Rust的工程师的支持性社区中受益匪浅,他们在我们遇到困难时提供帮助和指导。而且,随着我们团队的壮大,新的开发人员的学习曲线也会变小。我们不再像以前那样与多个库搏斗,现在我们有一个非常简单的文件,基本上说 "这里是如何使用这个单一的命令来构建一切"。

有了Rust,我们的开发者就像我们心爱的玛格丽特披萨一样,清楚地知道应该期待什么。

随着时间的推移,我们希望将更多的功能转移到我们的内部库。macOS的屏幕记录器已经用Rust重写了,Windows记录器的类似重写也正在进行中。我们也一直在将GIF创建和其他操作系统级的集成等功能移入我们的Rust库。我们对Windows上的Rust (windows-rs)的未来感到特别兴奋,它最近刚刚达到0.21.0版本

但这也不是一个全有或全无的方法。我们对Rust进行了配置,所以如果需要的话,我们仍然可以使用旧的shell进程方法调用第三方库。这意味着我们可以有意识地选择重写哪些功能以及何时重写。当然,如果我们对Rust感到挣扎,那就很容易回头了。但考虑到Rust所带来的好处,我们的热情和兴奋原来是有道理的。