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