Loading

Rust不适合开发Web API

2021-02-09 21:31:02 2263

kaiyun开云官方网页版_kaiyun和卓英软件(jiàn)
作者 | Tom MacWright
译者 | 吴留坡
策划 | 蔡芳芳
来源丨前端(duān)之巅(ID:frontshow)

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

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

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

缺失很多小(xiǎo)功能

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

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

有些库试(shì)图解(jiě)决这个问题,比如 libreauth,但它(tā)才刚(gāng)刚开始(shǐ)开发(fā)。还有(yǒu)很多类似的 Web 框架问题。

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

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

有人会说好吧,X 编(biān)程(chéng)语言太好了,你可以在周(zhōu)末自己写一个(gè) SDK!我必须回答,不。

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

Rust 编译器比(bǐ)以(yǐ)前(qián)快,但仍然很(hěn)慢(màn)

我一直在看(kàn) Nicholas Nethercote 的博客,描述了(le) Rust 团队如(rú)何优化编译器,让它更快!

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

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

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

Rust 很复杂

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

这(zhè)些(xiē)担忧都是(shì)合(hé)理的,但是对(duì)于大多数 Web 应用程序来说,它(tā)们并不是最重要的关注(zhù)点,以流行的惯性思考会导致不正确的假设。

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

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

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

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

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

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

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

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

Rust 生态系统不(bú)是以 Web 为中心的

很(hěn)多人正在学 Rust,用 Rust 编写 CLI 应(yīng)用程序或底层(céng)代码,并且(qiě)玩得非(fēi)常开心。使用 Rust 编写(xiě)普通 Web 应用程序的(de)人明显少很多。

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

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

Juniper 的(de) N+1 次查询

这一部分不仅(jǐn)仅是 Rust,它还涉及 GraphQL 生态系统,Rust 参与这个生态系统就是一(yī)个例(lì)子。

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

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

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

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

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

一(yī)些注意事项

首先,本(běn)文提(tí)到的(de)问(wèn)题并不(bú)针(zhēn)对在通用(yòng)场(chǎng)景使用 Rust,只针对将 Rust 用(yòng)于特定目标和生态系统,简单说就(jiù)是 Web API。

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

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

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

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

 延伸阅读

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


">

    kaiyun开云官方网页版_kaiyun(中国)

    kaiyun开云官方网页版_kaiyun(中国)