Loading

Rust不适合开发(fā)Web API

2021-02-09 21:31:02 2262

PT真人和卓英软件
作者 | Tom MacWright
译(yì)者 | 吴留坡
策划 | 蔡芳芳
来源丨前(qián)端(duān)之巅(ID:frontshow)

Rust 是一门神奇的编程语言(yán),有非常好的 CLI 工具,比(bǐ)如 ripgrep 和 exa。像 Cloudflare 这样的(de)公司正在使用并 鼓励(lì)人们写 Rust 来(lái)运行微服务(wù)。Rust 编(biān)写(xiě)的软件可能比(bǐ) C++ 或 C 更(gèng)安全、更小、更简洁。

如果我正在(zài)编写一个地(dì)理编码器、一个路由引(yǐn)擎、一个实(shí)时(shí)消息平台、一个数据库或一(yī)个 CLI 工(gōng)具,Rust 最合适(shì)。

但去年,我(wǒ)试图用 Rust 写一(yī)个传统网站的纯 API 服(fú)务(wù),Rust 就不合(hé)适了。

缺失很多小功能

Rust 有大量的 Web 服务框架、数据库连接器和解(jiě)析器。但搭建身份验证服务方面(miàn)只有非常低层次的组件。Node.js 有(yǒu) passport.js,Rails 有 devise,Django 有 开箱即用的身(shēn)份验证(zhèng)模型,在 Rust 中,你需(xū)要学(xué)习如何将共享 Vec 转换到底层加密(mì)库才(cái)能构建这个系统。

译者注,Vec 是(shì)一个动态数组,只会自动增长而不(bú)会自动收缩。区别于 Array,Vec 具(jù)有动态的添加和删(shān)除元素(sù)的能(néng)力,并且能够以 O(1) 的效率进行随机(jī)访(fǎng)问。Vec 的所有(yǒu)内容项都是生成在堆空间上的,可(kě)以轻易的将 Vec 移(yí)出一个栈而不用担心内存拷贝影响执行效(xiào)率(lǜ),毕竟(jìng)只(zhī)是拷贝栈上的(de)指针。

有些库试图解决这个(gè)问(wèn)题,比如 libreauth,但它才刚刚开始开发。还有很多类似(sì)的 Web 框(kuàng)架问题。

SDK 呢?在主流(liú)编程语言中,你可以通过一个官方库(kù)来接入 Google 云(yún)服务(wù)、AWS 或 Stripe。这些官(guān)方(fāng)库(kù)大都很棒。例如,aws-sdk-js 和(hé) Stripe 库(kù)的设计和维护得非常好(hǎo)。

Rust 就(jiù)不这(zhè)样,只(zhī)有少许第三方库,但以这些服务的开发速度,它们(men)真的能够提供高质量的体验吗?

有人会说(shuō)好吧,X 编程语言太好(hǎo)了,你可以在周末自己(jǐ)写一个 SDK!我必须(xū)回答,不。

Rust 的生态系统在其它领域非常丰富。用(yòng)于构(gòu)建 CLI、管理并发性、使用二进(jìn)制数据和底层解析器(qì)的 crates 令人印象深刻,非常(cháng)棒。

Rust 编(biān)译(yì)器比以前快,但仍然很(hěn)慢

我一直在看 Nicholas Nethercote 的博(bó)客,描(miáo)述了(le) Rust 团队如何优化编(biān)译器,让它更快!

但与其它(tā)编程语言相比,用(yòng)它构建网站会很慢。它(tā)比编译型编程语(yǔ)言(yán) Go 慢得多,也(yě)比解释型编程(chéng)语言 JavaScript、Ruby 和(hé) Python 等慢(màn)得多。

一旦代码被编译,一切就(jiù)变得非常棒了!但在我的情况下,甚(shèn)至基本 API 功能(néng)都不完整,一个不复杂的系统——居然花了 10 多分(fèn)钟(zhōng)来编译。Google 代码构建 的硬件配置很差,每次都会(huì)超时,我(wǒ)啥都(dōu)编译不(bú)了。

只要不重建缓存依赖(lài)项,缓存(cún)就有意义(yì)。也许(xǔ) 减少依(yī)赖 会加(jiā)快 Rust 项目编译。但就像 serde,几乎所有人都使(shǐ)用的 JSON 和(hé)其它(tā)序列化 / 反(fǎn)序列化程序占用了大量(liàng)的(de)编译时间。我们是否应该用编译速度更快但缺乏大量文档和生态(tài)系(xì)统支持(chí)的东西来(lái)取代(dài) serde?这种取舍非(fēi)常糟(zāo)糕(gāo)。

Rust 很复杂

Rust 让你(nǐ)从代码维度进行思考,这对系统编程来(lái)说非(fēi)常重(chóng)要。它让你思(sī)考如何(hé)共享或复制(zhì)内存,思考真实但不太可(kě)能的小概率(lǜ)事件(jiàn),并(bìng)确保妥善处理它们,帮你编写各(gè)种(zhǒng)各样(yàng)的高(gāo)效代码。

这些担忧都是合理的(de),但是对于大多数 Web 应用程(chéng)序来说,它(tā)们并不是最重(chóng)要的关注(zhù)点(diǎn),以流行的惯(guàn)性思考会导致不正确的假设。

就拿 Rust 的安(ān)全性来说吧。这是它宣传语中的重要部(bù)分,这是绝对正确的:Rust 的承诺安(ān)全和底层两者(zhě)兼(jiān)而有(yǒu)之——它可以(yǐ)在(zài)没有垃圾收集器的情况下工作,同时防(fáng)止基(jī)于(yú)内存的漏洞。当你读到(dào)“安全”的时候,想想 Rust 的竞争对(duì)手 C 吧。C 语言中的代码(mǎ)可以引用(yòng)任(rèn)意内存(cún),很(hěn)容易溢出和出错。Rust 代码可以和 C 代(dài)码一样快,但(dàn)是可以保护内存访问,而(ér)不需要垃圾收集器(qì)或某(mǒu)种运行时检查。

但是 Rust 的内(nèi)存(cún)规则(zé)并不比 Node.js 或(huò) Python 更安全,用 Rust 编(biān)写的(de) Web 应用程序在系统上不会比(bǐ) Python 或(huò) Ruby 应(yīng)用(yòng)程序安(ān)全(quán)。带有垃圾(jī)收(shōu)集器的高(gāo)级编程(chéng)语言通常(cháng)为避免这类漏(lòu)洞利用和(hé)错误而(ér)付出(chū)性能损失。不能在 JavaScript 中引(yǐn)用未初始化(huà)的内(nèi)存,因为 JavaScript 中不进行内存间的引用。

旁注:这是在描述(shù) Node.js 和其它系统的设计目标——它们确实偶尔(ěr)会有(yǒu) bug。Node.js 的缓存对象,就值(zhí)得读一读。

你要是 问(wèn)一些(xiē)人,他们会(huì)说如果(guǒ)使(shǐ)用不安全的代码,Rust 相比(bǐ)带有内(nèi)存(cún)回收的(de)编程语言(yán)是不安全的——包括最(zuì)流行的 Web 框架(jià) Actix(译者注,Actix 是 Rust 的 Actor 异步并(bìng)发框架,基于 Tokio 和 Future,开箱具(jù)有异步非阻塞(sāi)事件驱动并发能力(lì),其实现(xiàn)低层级 Actor 模(mó)型(xíng)来(lái)提供无(wú)锁并发模(mó)型,而且同时提供同(tóng)步 Actor,具有快(kuài)速、可靠,易可(kě)扩(kuò)展 https://actix.rs/),因为 不安全代(dài)码允许原始(shǐ)指针的(de)延迟。

如果(guǒ)你正在写一个视频(pín)游(yóu)戏,暂停执(zhí)行垃圾(jī)收集是不好的。如果你在编(biān)写微控(kòng)制(zhì)器代码,任何内存“开(kāi)销”或浪(làng)费都是非常糟糕的。但是大多数 Web 应用(yòng)程序(xù)可以节省一点内存开销(xiāo)来换取生产性能。

Rust 的其它属性面对的争(zhēng)议几乎一样。它(tā)的并发特性是太神奇了,如果你(nǐ)在(zài)做一些复杂的事情(qíng),需要快速响应(yīng),这当(dāng)然很棒。但如果情况(kuàng)不(bú)是这样呢?至少可以说,Rust 的异步生态系统(tǒng)面(miàn)临着很大挑战(zhàn):各种不相关的领域中有着不同的异步实(shí)现,比如 tokio。

相比较之下,Python 的(de) Tornado 和 Twisted 异步实现的很(hěn)奇怪,Node.js 异步实(shí)现的很好(hǎo),但语法(fǎ)都很丑陋。

我确信,Rust 的(de)异步(bù)将(jiāng)会稳定和(hé)统一,未来会更(gèng)容易操作,但(dàn)我现在(zài)就要用啊。

Rust 生态系统不是(shì)以 Web 为中心的

很(hěn)多人(rén)正在学 Rust,用 Rust 编写 CLI 应用程序或底层代(dài)码,并且玩得非常开心。使用 Rust 编写普通 Web 应用(yòng)程序的人明显少很(hěn)多(duō)。

这是技术选择中的重要(yào)部分:是否有人在使用该工具?他们大致在同一(yī)个领(lǐng)域吗?不(bú)幸的是,Rust 生(shēng)态系统中许多令人难以置(zhì)信的(de)令人兴(xìng)奋(fèn)的工作(zuò)与(yǔ) Web 应用服务器无关。的确存在一些很有(yǒu)前途(tú)的 Web 框架(jià)——甚至更高层(céng)次的框架,但(dàn)毫无疑问,它们市(shì)场很小。即使是主要的 Web 框架 Actix 也只有几个顶尖(jiān)贡献者(zhě)。

如果 Rust 以目前的速度增长,那么社区中的 Web 部分将(jiāng)达到一个临界值,但我认为(wéi)没有足(zú)够多的人(rén)使(shǐ)用 Rust 作为网站的实(shí)用(yòng)工具。与其(qí)它社区(qū)相(xiàng)比,有很多(duō)公司致力于使用现有(yǒu)的工具来构建 Web 应用程序,这些(xiē)工具(jù)不是(shì)最(zuì)前沿(yán)的,但(dàn)足(zú)够将成熟技术与新技术区分(fèn)开来。

Juniper 的 N+1 次查询

这(zhè)一部分(fèn)不仅仅是(shì) Rust,它还涉及 GraphQL 生态系(xì)统,Rust 参与这个生(shēng)态系统就(jiù)是一个例(lì)子。

N+1 问题(tí) 是每个构(gòu)建(jiàn) Web 应用程序(xù)的人都应该知道的。要(yào)点是:你有一页(yè)照片(一次(cì)查(chá)询(xún)),你要显(xiǎn)示每张照(zhào)片的作(zuò)者,会(huì)有多少次查(chá)询:1,合并照片(piàn)和作者,或者(zhě)在检索(suǒ)照(zhào)片后对每(měi)张(zhāng)照片进行查询以获取作者?或(huò)者两次,第二次查询 ids 中的(de) user.id,一次获取所有(yǒu)作者,然后重新设(shè)置他们的照片属性(xìng)。

N+1 查询通常优先使用(yòng)数据库解决(jué):比(bǐ)如将 N+1 查询改为单个查询,会(huì)带来明显的性能优化。我们有很多(duō)方(fāng)法来尝试和解决这些问题:你可以编写(xiě) SQL,并(bìng)尝试使用 CTE 和 JOIN 在单个查询中完成大量工作,就(jiù)像我(wǒ)们在 Observable 中所做的那(nà)样,或者使用像 ActiveRecord 这(zhè)样的 ORM 层将 N+1 查询转换(huàn)为(wéi)可预测查询的快速方(fāng)法。

Juniper 是一个用于 Rust 应用程序(xù)的 GraphQL 服务。GraphQL 基本上都是由(yóu)前端应用(yòng)程序定义查询,而(ér)不是后端。给(gěi)它一系列可以查询的东西,然后(hòu)应用程序(React 或其它)将任意查询发(fā)送到后端。

这会让后端(duān)变得复杂。任何 SQL 级别(bié)的优化都不可能做到——你的(de)服务(wù)器正在编写动态 SQL,优化只能依赖 GraphQL 服务,但它(tā)不会总是有效。例如(rú):Juniper 默认情(qíng)况下执行的是 N+1 查询,解决方(fāng)案 dataloader 还比较粗糙且需要单独维护。因(yīn)此,最终您将拥有一个非常快(kuài)的应用程序(xù)层,但(dàn)它所有的时间都花在了极(jí)其低效的数(shù)据(jù)库查(chá)询(xún)上。

总之,GraphQL 与 NoSQL 数据库(kù)配合使(shǐ)用效(xiào)果(guǒ)非常(cháng)好,它可以快速为这些类型的请求提供(gòng)服务。我确信 Facebook 内(nèi)部有一些特定的数据(jù)库与 GraphQL 结合在(zài)一起使(shǐ)用(yòng)效果非常棒,但(dàn)业内(nèi)其(qí)他企业则非常依赖 Postgres 和同类产品。

一些注意(yì)事项

首先(xiān),本文提到的(de)问题并不针对(duì)在(zài)通用场景使用 Rust,只针对将 Rust 用于(yú)特定目标和生(shēng)态系统,简单(dān)说就是 Web API。

注意事项 1:一般(bān)情(qíng)况(kuàng)下,你可以(yǐ)用(yòng)任何编程(chéng)语言搭建网站,还记得基于(yú) C++ 实现的OkCupid 吗?(译(yì)者注,OkCupid 是(shì)美国一(yī)个(gè)大型线上交友网站)还有(yǒu)一个非常流行的 星象应用程序(xù),Co-star,它全部是用 Haskell 编写的。如果你擅(shàn)长其它编程语言(yán),或者可以招聘到擅长这些编程语(yǔ)言(yán)的工程(chéng)师,你(nǐ)一(yī)样可以取得成功。

注意事(shì)项 2:我试图构建的是重 CRUD(增删改查) 的 Web 应用程序 API。它可能不算是一个 Web“服(fú)务”——主要是(shì)快(kuài)速、无数次地(dì)执行同一个操(cāo)作,而是一个 Web“应用程序”——执行了许多不同的(de)操作,包含了(le)相当多的业务逻(luó)辑。如果你要开发(fā)的东西(xī)跟(gēn)我在做的不一样,那我的建议可能就不适合(hé)你。如果你需要的(de)是快速(sù)执行一两个操作,比如你正在写一(yī)个支(zhī)付网关(guān)或语音消息应用程序,那 Rust 可能效果还是不错的。

注(zhù)意事项 3:这篇文章写于 2021 年(nián) 1 月,如果接下来社区继续发展,Rust 将得(dé)到持续的改进(jìn),会变得更好(hǎo)并更易于(yú) Web 应用程序开发。

总而言之,我真的很喜(xǐ)欢使用 Rust,这是(shì)一(yī)门美(měi)丽的编程语言(yán),有很(hěn)多很酷的想(xiǎng)法。希望很(hěn)快,Rust 会成为(wéi)能(néng)用(yòng)来构建我(wǒ)想做的东西的最合适的工具。不过,现(xiàn)在我想做的很多东西都要采用不同特性(xìng)的编程语言(yán)才能更好(hǎo)地运行。

 延(yán)伸阅读

https://macwright.com/2021/01/15/rust.html


">

    PT真人(中国)官方网站

    PT真人(中国)官方网站