Loading

Rust不(bú)适合(hé)开发Web API

2021-02-09 21:31:02 2262

开云网页版页面登录和卓英软件
作者 | Tom MacWright
译者 | 吴留坡
策划 | 蔡芳芳(fāng)
来源丨前端之巅(ID:frontshow)

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

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

但(dàn)去年(nián),我试图(tú)用 Rust 写一个传统网站的(de)纯 API 服务,Rust 就不合适了。

缺失很多小功能

Rust 有(yǒu)大量的(de) Web 服务框架、数据库连接(jiē)器和解析(xī)器。但搭建身份验证(zhèng)服(fú)务方(fāng)面只有非常低层次的组件。Node.js 有 passport.js,Rails 有 devise,Django 有 开箱即(jí)用的身份验证模型(xíng),在 Rust 中,你需要学习如何将共享 Vec 转(zhuǎn)换到底(dǐ)层加密库才能构(gòu)建这个系统。

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

有(yǒu)些(xiē)库试图(tú)解决这个问题,比如(rú) libreauth,但它才刚刚开(kāi)始开发(fā)。还有很多类(lèi)似的 Web 框架问题。

SDK 呢?在主流编程语言中,你(nǐ)可以通(tōng)过一个官方库来接(jiē)入 Google 云服(fú)务、AWS 或 Stripe。这些官方库大都(dōu)很(hěn)棒。例如,aws-sdk-js 和 Stripe 库的设计和维护得(dé)非常好。

Rust 就不(bú)这样,只有少许第(dì)三方库,但以这些服务的开(kāi)发速度,它们真的能(néng)够提供高质量(liàng)的体验(yàn)吗?

有人(rén)会说好吧,X 编程语言太好了(le),你可以在周末自己写一个 SDK!我必须回答(dá),不。

Rust 的生态(tài)系统在其它领域非常丰富。用于(yú)构建 CLI、管理(lǐ)并发性(xìng)、使用二进制数据和底层解(jiě)析(xī)器的 crates 令人印象深刻,非常棒。

Rust 编译器比以前快,但仍然(rán)很慢

我一(yī)直(zhí)在看 Nicholas Nethercote 的博客(kè),描述了(le) Rust 团队如何(hé)优(yōu)化编(biān)译器,让它更(gèng)快!

但与其它(tā)编程语言相比,用它构建网站会(huì)很(hěn)慢。它比编译型编程语言(yán) Go 慢得(dé)多,也(yě)比(bǐ)解(jiě)释(shì)型编程语言(yán) JavaScript、Ruby 和 Python 等慢得多。

一旦代码被编译,一切就(jiù)变得(dé)非常棒了!但在我的(de)情况下,甚至基本 API 功能都不(bú)完(wán)整,一个不(bú)复杂的(de)系统——居(jū)然花了(le) 10 多(duō)分钟来编(biān)译。Google 代码构建 的硬件配置(zhì)很差,每(měi)次都会超(chāo)时,我啥都(dōu)编译(yì)不了(le)。

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

Rust 很复杂(zá)

Rust 让你(nǐ)从代码维度进行思考,这对系统编程来(lái)说非常重要(yào)。它让你思考如何共享或复制内存(cún),思考真实但(dàn)不太可(kě)能的小(xiǎo)概率事件,并确(què)保妥(tuǒ)善处理它们,帮你编写各种各样的高效代码。

这些担忧都是合理的,但是对于大多数(shù) Web 应用程序来说(shuō),它们并不是最重(chóng)要的(de)关注点,以流行的惯性思(sī)考会导致不正确(què)的假设。

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

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

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

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

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

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

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

我确信,Rust 的异步将(jiāng)会稳定和统一,未来会(huì)更容易操作,但我(wǒ)现在就要用啊(ā)。

Rust 生(shēng)态系统不是以 Web 为中心(xīn)的(de)

很多人(rén)正在学 Rust,用 Rust 编写 CLI 应用程(chéng)序(xù)或底层(céng)代码(mǎ),并且玩(wán)得非常开(kāi)心。使用 Rust 编写普通 Web 应(yīng)用程序(xù)的人明显(xiǎn)少很(hěn)多。

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

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

Juniper 的 N+1 次查询

这一(yī)部分不仅仅是(shì) Rust,它还涉及 GraphQL 生态系统,Rust 参与这(zhè)个(gè)生态系统就是(shì)一个例子。

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

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

Juniper 是一个用(yòng)于 Rust 应用程(chéng)序的 GraphQL 服(fú)务。GraphQL 基本上(shàng)都是由前端应用程序(xù)定义查询,而不是后端。给它一(yī)系列可以查(chá)询的东(dōng)西,然后应用程序(React 或其它)将任意查询发送到后端。

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

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

一些注意事(shì)项

首(shǒu)先,本(běn)文提到(dào)的问题并不针对(duì)在通(tōng)用场景使用 Rust,只针对将 Rust 用于特定目标和生态系(xì)统,简单说就(jiù)是 Web API。

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

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

注意事(shì)项 3:这篇(piān)文(wén)章写于 2021 年(nián) 1 月,如(rú)果(guǒ)接下来(lái)社(shè)区继续发展(zhǎn),Rust 将得到(dào)持续的改进,会变得更好并更(gèng)易(yì)于 Web 应(yīng)用程(chéng)序开发。

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

 延伸阅读

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


">

    开云网页版页面登录-开云(中国)

    开云网页版页面登录-开云(中国)

    <bfo class="sdtvm"></bfo>