Loading

Rust不适合开(kāi)发Web API

2021-02-09 21:31:02 2262

开云手机站入口和卓英软件(jiàn)
作者 | Tom MacWright
译者 | 吴留(liú)坡
策划(huá) | 蔡芳芳
来源丨(shù)前端之巅(ID:frontshow)

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

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

但(dàn)去年,我试图用 Rust 写(xiě)一(yī)个(gè)传统网站的纯(chún) API 服(fú)务,Rust 就不合适了(le)。

缺失很(hěn)多小功能(néng)

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

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

有些库试图解决这个问题,比如 libreauth,但它才刚刚(gāng)开(kāi)始开发(fā)。还有很多类(lèi)似的 Web 框架(jià)问题。

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

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

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

Rust 的生态系统在(zài)其它领域(yù)非常丰富。用(yòng)于构建 CLI、管理(lǐ)并(bìng)发(fā)性、使用二进制数据和底(dǐ)层解析器的 crates 令(lìng)人印象深(shēn)刻,非常(cháng)棒。

Rust 编译器(qì)比(bǐ)以(yǐ)前(qián)快(kuài),但(dàn)仍然(rán)很慢

我(wǒ)一直在看 Nicholas Nethercote 的(de)博客,描述了 Rust 团队如何(hé)优化编译器,让(ràng)它(tā)更快(kuài)!

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

一旦代码被编译,一切就(jiù)变(biàn)得非常棒(bàng)了!但在我(wǒ)的(de)情(qíng)况下,甚至基本 API 功能都不完整,一个(gè)不复杂的系统——居然花(huā)了 10 多分钟来编译。Google 代码构建 的硬件配(pèi)置很差(chà),每(měi)次(cì)都会(huì)超时,我啥都编译(yì)不了。

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

Rust 很复杂

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

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

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

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

旁(páng)注:这是(shì)在(zài)描述 Node.js 和其它(tā)系统的设计目(mù)标——它们确实偶尔会有(yǒu) bug。Node.js 的缓存(cún)对(duì)象,就值得读一读。

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

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

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

相比较之下,Python 的 Tornado 和 Twisted 异步(bù)实现的很奇怪,Node.js 异步实现(xiàn)的很(hěn)好,但(dàn)语法都(dōu)很丑陋。

我确信,Rust 的异(yì)步将会稳(wěn)定和统一,未来会更容易操作,但(dàn)我现在就(jiù)要用啊。

Rust 生态(tài)系(xì)统不是以 Web 为中心的

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

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

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

Juniper 的 N+1 次查询

这(zhè)一部分不仅仅是 Rust,它还涉及 GraphQL 生态系统,Rust 参与(yǔ)这个生态(tài)系统就是一个例子(zǐ)。

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

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

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

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

总之,GraphQL 与 NoSQL 数据(jù)库配(pèi)合使用效果非常好,它可以快速为这(zhè)些类型的请求提供服务。我确信 Facebook 内部有一些特定的数据库与 GraphQL 结合在一起使用效(xiào)果非常棒,但业内其他企业则非常依赖 Postgres 和(hé)同类产品。

一(yī)些注意事(shì)项

首(shǒu)先,本文(wén)提(tí)到的(de)问题并不针对(duì)在通用场景(jǐng)使用 Rust,只(zhī)针对将 Rust 用于(yú)特定目标和(hé)生态系统(tǒng),简单说就是 Web API。

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

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

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

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

 延伸阅读

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


">

    开云手机站入口-开云(中国)

    开云手机站入口-开云(中国)