Rust 是(shì)一门神奇的编程(chéng)语言(yán),有(yǒu)非常好的 CLI 工具,比(bǐ)如 ripgrep 和 exa。像 Cloudflare 这(zhè)样的公司正在使(shǐ)用(yòng)并 鼓励人们写(xiě) Rust 来运行(háng)微服务。Rust 编写(xiě)的软件(jiàn)可(kě)能比(bǐ) C++ 或 C 更安全(quán)、更小、更简洁。
如果我正在编写一个地理编(biān)码器、一个(gè)路由引擎(qíng)、一个实时消息平台、一个数(shù)据库或一个 CLI 工具,Rust 最合适。
但去(qù)年,我试图用 Rust 写一个传统网站(zhàn)的(de)纯 API 服(fú)务,Rust 就不合(hé)适了。
Rust 有(yǒu)大(dà)量的(de) Web 服务框架(jià)、数据库(kù)连接器和解析(xī)器。但搭建身(shēn)份(fèn)验证服(fú)务方面只(zhī)有非常低层次(cì)的组件。Node.js 有 passport.js,Rails 有 devise,Django 有(yǒu) 开箱即用的(de)身份(fèn)验证模型,在 Rust 中,你需(xū)要学习如何(hé)将(jiāng)共享 Vec 转换到底层加密库才能(néng)构建这个系统。
译(yì)者(zhě)注,Vec 是(shì)一(yī)个动(dòng)态数组,只(zhī)会自动增长而(ér)不会自动收缩。区别于 Array,Vec 具有(yǒu)动态的(de)添加(jiā)和删除元素的能力,并且能够(gòu)以 O(1) 的效率进行(háng)随机访问。Vec 的(de)所有内容(róng)项都是生成在(zài)堆空间(jiān)上的,可以(yǐ)轻易的将 Vec 移(yí)出一个(gè)栈而不(bú)用担(dān)心内存拷贝影(yǐng)响(xiǎng)执行(háng)效率,毕(bì)竟只(zhī)是拷贝栈(zhàn)上的指针。
有些库试(shì)图解(jiě)决这(zhè)个(gè)问题(tí),比如 libreauth,但它才刚刚开始开发。还有(yǒu)很多类似的 Web 框架问题。
SDK 呢?在主流编程(chéng)语(yǔ)言中(zhōng),你可以通过一个官方库来接(jiē)入 Google 云(yún)服务、AWS 或 Stripe。这些(xiē)官方库大(dà)都很(hěn)棒。例如,aws-sdk-js 和 Stripe 库的设计和维护得非常好。
Rust 就不这样,只有少(shǎo)许第三方库,但以这些服务的开发速度,它们真的能(néng)够提供(gòng)高质量的体验吗?
有人会说好(hǎo)吧,X 编程语言太好了,你可以在周末(mò)自己写一(yī)个 SDK!我必须回答,不。
Rust 的(de)生态系统在其它领(lǐng)域非常丰富。用于构建 CLI、管理并发性、使用二(èr)进制数据和(hé)底层解析器的(de) crates 令人印象(xiàng)深刻,非(fēi)常棒。
我一直在看 Nicholas Nethercote 的(de)博客,描述了 Rust 团队如何(hé)优化(huà)编译器,让它更快!
但(dàn)与其它编(biān)程(chéng)语(yǔ)言(yán)相比,用它(tā)构建网(wǎng)站会很慢。它比编译型编程(chéng)语言 Go 慢得多,也(yě)比解释型(xíng)编程语言(yán) JavaScript、Ruby 和 Python 等(děng)慢得多。
一旦代(dài)码(mǎ)被(bèi)编译(yì),一切就变得非常棒了!但在我的情(qíng)况下,甚至基本(běn) API 功(gōng)能都(dōu)不完整,一个不复(fù)杂的系统——居然(rán)花了 10 多分钟来(lái)编译(yì)。Google 代码构建 的硬件(jiàn)配置很差,每次都会超时(shí),我啥都编(biān)译不了。
只要不(bú)重建缓存依赖项,缓(huǎn)存就有意义。也许 减少(shǎo)依赖 会加快(kuài) Rust 项目编译。但就像 serde,几乎所有人都使用的 JSON 和(hé)其它序列化 / 反(fǎn)序(xù)列化程序占用了大量的编译时间(jiān)。我(wǒ)们(men)是否应该用(yòng)编译速度更快但缺乏大量文档和生态系统支持的东西来取(qǔ)代 serde?这(zhè)种取舍(shě)非常糟糕。
Rust 让(ràng)你从代码(mǎ)维度进(jìn)行思考,这对系统编(biān)程来说非常(cháng)重要。它让你思考如何共享(xiǎng)或复制内存,思(sī)考真(zhēn)实但不(bú)太(tài)可(kě)能的(de)小(xiǎo)概率事(shì)件,并确保妥(tuǒ)善处理它们,帮你(nǐ)编写各种各样(yàng)的高效(xiào)代(dài)码。
这些(xiē)担忧都是合理的,但是对于大多数 Web 应用程序来说,它们并不是最重要的关注点,以流行的惯性思考会导致不正确的假设。
就拿(ná) Rust 的安全性(xìng)来说(shuō)吧。这是(shì)它宣传语中的(de)重(chóng)要部分,这是绝对正确的:Rust 的(de)承诺(nuò)安全和底层两者兼而有之——它可(kě)以(yǐ)在没(méi)有垃圾收集器的情况下工作,同时防止基于内(nèi)存的漏(lòu)洞。当(dāng)你读到“安全”的(de)时候(hòu),想想 Rust 的(de)竞争(zhēng)对(duì)手(shǒu) C 吧。C 语(yǔ)言(yán)中(zhōng)的代码可以引用任意内存,很容易溢出和出错。Rust 代(dài)码可以和 C 代(dài)码(mǎ)一样快(kuài),但是可(kě)以(yǐ)保护内存访(fǎng)问,而不需要垃圾收(shōu)集器或(huò)某种(zhǒng)运行时检查。
但(dàn)是 Rust 的内存(cún)规(guī)则并不比(bǐ) Node.js 或 Python 更安全(quán),用 Rust 编(biān)写(xiě)的 Web 应用程序在系统上不(bú)会比 Python 或(huò) Ruby 应用程序安全。带(dài)有垃圾收集器的高级编程语言通常为避免这类漏(lòu)洞利用和错误而付出性能损失。不能在 JavaScript 中(zhōng)引(yǐn)用未(wèi)初始化的内存,因为(wéi) JavaScript 中不(bú)进行内存间的引用(yòng)。
旁注:这(zhè)是(shì)在描述 Node.js 和(hé)其它系(xì)统的设计目标——它们(men)确(què)实偶(ǒu)尔(ěr)会有 bug。Node.js 的缓存对象,就(jiù)值得读一(yī)读。
你要是 问一些人(rén),他们会说如(rú)果使用不(bú)安全的代码,Rust 相(xiàng)比带有内存回收的编程语言是不安全的——包括最流行的 Web 框架 Actix(译者注,Actix 是 Rust 的(de) Actor 异步(bù)并发框架,基(jī)于 Tokio 和 Future,开(kāi)箱(xiāng)具有(yǒu)异步非(fēi)阻塞事件驱动并发能力,其实现低层(céng)级 Actor 模型(xíng)来提(tí)供(gòng)无锁并发模(mó)型,而且同时提供同步(bù) Actor,具(jù)有快速、可靠,易可扩展 https://actix.rs/),因为 不安全代码允许原始指针的延迟。
如(rú)果你正在(zài)写一个视频游戏,暂停执行垃圾收集是不好的。如果(guǒ)你(nǐ)在(zài)编写微控制器代码(mǎ),任何内存“开销”或浪费都是非常糟糕的(de)。但是(shì)大多数 Web 应用程序可以节省一点内存开销来换(huàn)取生产性能。
Rust 的其它属性面对的争议几乎一样。它的并发(fā)特性是太神奇了,如果你在做一些(xiē)复杂的(de)事情,需要(yào)快速响应,这当然(rán)很棒。但如果情况不是这样(yàng)呢?至少可以说,Rust 的异步生(shēng)态(tài)系统面临着很大挑战(zhàn):各种不相关的领域(yù)中有着不同的异步(bù)实现,比如 tokio。
相(xiàng)比较之下,Python 的(de) Tornado 和(hé) Twisted 异步(bù)实现的很奇怪,Node.js 异步实现的很好(hǎo),但语法都很丑陋。
我确(què)信,Rust 的异步将会稳定和统一,未来会更(gèng)容(róng)易操(cāo)作,但我(wǒ)现在就要用啊。
很多人正在学 Rust,用 Rust 编写 CLI 应(yīng)用程序或底层代码,并(bìng)且玩得非(fēi)常(cháng)开(kāi)心。使用(yòng) Rust 编写普通 Web 应(yīng)用程序的人(rén)明(míng)显少很多。
这(zhè)是技术选(xuǎn)择(zé)中的(de)重要部分:是否有(yǒu)人在(zài)使用(yòng)该工具?他们大致在同一个领域吗?不幸的是(shì),Rust 生态系(xì)统中许多(duō)令人(rén)难以(yǐ)置(zhì)信的令人兴奋的工作与 Web 应用服务器无(wú)关。的(de)确存在一些很(hěn)有前途的(de) Web 框架(jià)——甚至(zhì)更高层次的框架(jià),但毫无疑问,它们市(shì)场很(hěn)小(xiǎo)。即使是主要(yào)的(de) Web 框(kuàng)架 Actix 也只有(yǒu)几个(gè)顶尖(jiān)贡献(xiàn)者(zhě)。
如(rú)果(guǒ) Rust 以目前的速度增长,那(nà)么社区中的 Web 部分将达到一个临界值,但我(wǒ)认为没有(yǒu)足够多的人使用(yòng) Rust 作为网(wǎng)站的实用工具。与其它社区相比,有很多公司致力于使用现有的(de)工具来构建 Web 应用程序,这些工具不(bú)是最前沿(yán)的,但足(zú)够将(jiāng)成熟技术与新(xīn)技术区(qū)分开来。
这一部分不(bú)仅(jǐn)仅是(shì) Rust,它(tā)还(hái)涉及 GraphQL 生态系统,Rust 参与这个(gè)生(shēng)态系统就是一(yī)个例子(zǐ)。
N+1 问题 是每个构建 Web 应用程(chéng)序的(de)人都应该知道(dào)的。要(yào)点是:你有一页照片(一次查(chá)询),你要显示每张(zhāng)照片(piàn)的作者,会有多(duō)少次查询(xún):1,合并照片(piàn)和作者,或者在检(jiǎn)索照片后对每张(zhāng)照(zhào)片进行查(chá)询(xún)以(yǐ)获取(qǔ)作者?或者(zhě)两次,第二次查询 ids 中的(de) user.id,一(yī)次(cì)获取所有(yǒu)作者,然后重新设置他们的照片属性。
N+1 查(chá)询(xún)通常优先使(shǐ)用(yòng)数据(jù)库解(jiě)决:比如将 N+1 查询改为单个(gè)查询(xún),会(huì)带来明显(xiǎn)的(de)性能优化(huà)。我们有很多方法来尝试和解(jiě)决这些问题:你可以编写 SQL,并尝试使用 CTE 和 JOIN 在单个查询(xún)中(zhōng)完成大(dà)量工作,就像我(wǒ)们在 Observable 中(zhōng)所做的那样,或者(zhě)使用像 ActiveRecord 这样(yàng)的 ORM 层将(jiāng) N+1 查询转换为可预测查询的快速方法。
Juniper 是(shì)一个用于 Rust 应用程(chéng)序(xù)的 GraphQL 服(fú)务。GraphQL 基本(běn)上都是由前端应用(yòng)程序定义查询,而不是后端(duān)。给(gěi)它一系列可以查询的东西,然后应(yīng)用(yòng)程(chéng)序(React 或(huò)其它)将任意查(chá)询发送到后(hòu)端。
这(zhè)会(huì)让后端变得复杂。任何 SQL 级别的优(yōu)化都不可能做到——你的(de)服务(wù)器正在编写(xiě)动(dòng)态 SQL,优(yōu)化只能(néng)依赖 GraphQL 服务,但它(tā)不(bú)会总是有效。例如:Juniper 默认情(qíng)况下执行的是 N+1 查询,解决(jué)方案 dataloader 还比较粗糙(cāo)且需要(yào)单独维护(hù)。因此(cǐ),最终您将拥(yōng)有一个非常快的应用程序层(céng),但它所有的时间都花(huā)在了极其低效的数(shù)据库查(chá)询上。
总之,GraphQL 与 NoSQL 数(shù)据库配合使用效果非(fēi)常(cháng)好(hǎo),它可(kě)以快速为这些类(lèi)型的请求提供服务。我确信 Facebook 内部有一些特定的数(shù)据库与(yǔ) GraphQL 结合在一起使用效果非常棒,但业内其他企业则非常依赖(lài) Postgres 和同类产品(pǐn)。
首先,本文(wén)提到的问题并不针对(duì)在通(tōng)用(yòng)场景(jǐng)使(shǐ)用 Rust,只针对将 Rust 用于(yú)特定目标和生态系统,简单(dān)说就(jiù)是 Web API。
注意事项 1:一般情况(kuàng)下,你(nǐ)可以(yǐ)用任何编程语言搭建网(wǎng)站,还记得基于 C++ 实现的OkCupid 吗?(译者注,OkCupid 是美国一个大型线上交友网站)还有一个非常流行的(de) 星象应(yīng)用程序,Co-star,它全部是用 Haskell 编写(xiě)的。如果你擅(shàn)长其它编(biān)程语言,或(huò)者可以招聘到擅长这些编程语言的(de)工程师(shī),你一样可以取得成(chéng)功。
注意(yì)事项 2:我试图(tú)构建的是重 CRUD(增删改查) 的 Web 应用程序 API。它可能不(bú)算是(shì)一个 Web“服务”——主要是快速、无数次地执行同一(yī)个操作,而是一个 Web“应用程序”——执行了许多不同的操作,包含了相当多的业务逻辑。如果你要开发(fā)的东西跟我在做的(de)不一样,那我的建(jiàn)议可能就不适合你。如果你需要的是快速执(zhí)行一两个(gè)操作,比如(rú)你正在写一个支付网关或语音消息(xī)应用程序,那 Rust 可能效(xiào)果还是不错的。
注意事项 3:这篇文章写于(yú) 2021 年 1 月,如果接下来社区继续发(fā)展,Rust 将得到持续(xù)的改进,会变得(dé)更(gèng)好(hǎo)并更易于 Web 应用程序开(kāi)发。
总而言之,我真的很喜欢使用 Rust,这是一(yī)门(mén)美丽(lì)的编程(chéng)语(yǔ)言,有很(hěn)多很(hěn)酷的想法(fǎ)。希(xī)望很(hěn)快,Rust 会成为能用来构建我想做(zuò)的东西的最合适的工具。不过(guò),现在我想做的很多(duō)东西都要采用不同特性的编程语言才能(néng)更好地运(yùn)行。
https://macwright.com/2021/01/15/rust.html