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