当前位置: 首页 > 科技观察

Rust不适合开发WebAPI

时间:2023-03-12 02:27:57 科技观察

Rust是一门令人惊叹的编程语言,拥有非常好的CLI工具,例如ripgrep和exa。Cloudflare等公司正在使用并鼓励人们编写Rust来运行微服务。用Rust编写的软件可能比C++或C更安全、更小、更简洁。如果我正在编写地理编码器、路由引擎、实时消息传递平台、数据库或CLI工具,Rust是最合适。但是去年我尝试用Rust为一个传统网站写一个纯API服务,Rust并不适合。缺少许多小功能Rust有大量的Web服务框架、数据库连接器和解析器。但是只有非常低级的组件可用于构建身份验证服务。Node.js有passport.js,Rails有devise,Django有开箱即用的身份验证模型,在Rust中你需要学习如何将共享Vec转换为底层加密库来构建这个系统。译者注,Vec是动态数组,只会自动增长不会自动收缩。与Array不同,Vec具有动态增删元素的能力,可以进行O(1)效率的随机访问。Vec的所有内容项都是在堆空间上生成的,Vec可以很容易地移出一个栈,而不用担心内存复制影响执行效率,毕竟它只是在栈上复制指针。有一些库试图解决这个问题,比如libreauth,但它才刚刚开始开发。有很多类似的网络框架问题。SDK呢?在主流编程语言中,您可以通过官方库访问GoogleCloudServices、AWS或Stripe。这些官方库中的大多数都很棒。例如,aws-sdk-js和Stripe库的设计和维护都非常好。Rust不是这样的,第三方库就那么几个,但是以这些服务的发展速度,真的能提供优质的体验吗?有人会说好吧,X编程语言这么好,周末自己写一个SDK吧!我必须回答,没有。Rust的生态系统在其他方面也很丰富。用于构建CLI、管理并发性、处理二进制数据和低级解析器的箱子令人印象深刻且棒极了。Rust编译器比以前更快,但仍然很慢我一直在阅读NicholasNethercote的博客,其中描述了Rust团队如何优化编译器使其更快!但是与其他编程语言相比,使用它构建网站可能会很慢。它比编译型编程语言Go慢很多,比JavaScript、Ruby、Python等解释型编程语言慢很多。编译代码后,一切都很棒!但就我而言,即使是基本的API功能也不完整,一个简单的系统——编译需要10多分钟。GoogleCode建立在糟糕的硬件配置之上,每次都超时,我无法编译任何东西。只要您不重建缓存依赖项,缓存就有意义。也许减少依赖会加快Rust项目的编译速度。但是像serde、JSON等几乎人人都用的序列化器/反序列化器会消耗大量的编译时间。我们是否应该用编译速度更快但缺乏大量文档和生态系统支持的东西替换serde?这是一个非常糟糕的权衡。Rust很复杂Rust让你从代码的角度思考,这对于系统编程非常重要。它让你思考如何共享或复制内存,思考真实但不太可能发生的小概率事件,并确保它们得到妥善处理,它帮助你编写各种高效的代码。这些担忧都是有道理的,但对于大多数Web应用程序而言,它们并不是最重要的担忧,并且以普遍的惯性思考会导致不正确的假设。以Rust安全为例。这是其标语的重要组成部分,而且绝对正确:Rust的承诺既安全又低级——它可以在没有垃圾收集器的情况下工作,同时防止基于内存的漏洞。当你读到“安全”的时候,想想Rust的竞争对手C,C中的代码可以引用任意内存,很容易溢出和出错。Rust代码可以和C代码一样快,但内存访问受到保护,不需要垃圾收集器或某种运行时检查。但Rust的内存规则并不比Node.js或Python更安全,用Rust编写的Web应用程序在系统上也不比Python或Ruby应用程序更安全。具有垃圾收集器的高级编程语言通常会为避免此类漏洞利用和错误而付出性能损失。您不能在JavaScript中引用未初始化的内存,因为JavaScript不引用内存。旁注:这是在描述Node.js和其他系统的设计目的——它们偶尔会出现错误。CacheObjectsforNode.js值得一读。如果你问一些人,他们会说,如果使用不安全的代码,Rust与带有内存收集的编程语言相比是不安全的——包括最流行的web框架Actix(译者注,Actix是Rust的ActorAsynchronous并发框架,基于Tokio和Future,具有开箱即用的异步非阻塞事件驱动并发能力,实现底层Actor模型提供无锁并发模型,同时提供同步Actor。快速、可靠且易于扩展https://actix.rs/),因为不安全代码允许延迟原始指针。如果您正在编写视频游戏,那么暂停垃圾收集是不好的。如果您正在编写微控制器代码,任何内存“开销”或浪费都是非常糟糕的。但是大多数Web应用程序可以节省一点内存开销以换取高效的性能。Rust的其他属性也面临着同样的争议。它的并发性是惊人的,如果你正在做一些需要快速响应时间的复杂事情,这当然很棒。但如果不是这样呢?Rust的异步生态系统具有挑战性,至少可以说:在各种不相关的领域中有不同的异步实现,比如tokio。相比之下,Python的Tornado和Twisted的异步实现就很奇怪了,Node.js的异步实现很好,但是语法很丑。我确信Rust的async将来会稳定统一,并且更容易使用,但我现在要使用它。Rust生态系统中许多不以网络为中心的人正在学习Rust,用Rust编写CLI应用程序或低级代码,并且玩得很开心。使用Rust编写常见Web应用程序的人明显减少了。这是技术选择的重要部分:有人在使用该工具吗?他们大致在同一个领域吗?不幸的是,Rust生态系统中许多令人难以置信的令人兴奋的工作与Web应用程序服务器无关。确实有一些有前途的Web框架——甚至更高级别的框架——但毫无疑问,它们的市场很小。即使是主要的网络框架Actix也只有少数顶级贡献者。如果Rust以目前的速度增长,社区的网络部分将会有一个临界质量,但我认为没有足够多的人将Rust作为网站的实用程序。与其他社区相比,有许多公司致力于使用现有工具构建Web应用程序,这些工具虽然不是最先进的,但足以将成熟技术与新技术区分开来。Juniper的N+1查询部分不仅仅是Rust,还有GraphQL生态,Rust的参与就是一个例子。N+1问题是每个构建Web应用程序的人都应该知道的。要点是:你有一页照片(一个查询),你想显示每张照片的作者,会有多少个查询:1,合并照片和作者,或者对每张照片进行查询以获得检索照片后的作者?或者两次,第二次查询ids中的user.id,一次获取所有作者,然后重置他们的照片属性。N+1查询通常首先使用数据库来解决:比如将N+1查询改为单个查询会带来显着的性能优化。我们有很多方法来尝试解决这些问题:你可以编写SQL并尝试使用CTE和JOIN在单个查询中完成大量工作,就像我们在Observable中所做的那样,或者使用像ActiveRecord这样的ORM层,它会快速将N+1查询转换为可预测查询的方法。Juniper是Rust应用程序的GraphQL服务。GraphQL基本上由前端应用程序而不是后端定义查询。给它一个它可以查询的列表,然后应用程序(React或其他)将任意查询发送到后端。这使后端复杂化。任何SQL级别的优化都是不可能的——你的服务器正在编写动态SQL,优化只能依赖GraphQL服务,这并不总是有效。例如:Juniper默认执行N+1个查询,解决方案dataloader比较粗糙,需要单独维护。所以你最终得到了一个非常快的应用层,但它所有的时间都花在了效率极低的数据库查询上。总之,GraphQL与NoSQL数据库配合得很好,可以快速处理这些类型的请求。我确信Facebook中的某些数据库与GraphQL配合得非常好,但该行业的其他数据库严重依赖Postgres和类似的数据库。一些注意事项首先,这篇文章中的问题不是关于在一般情况下使用Rust,而是关于将Rust用于特定目标和生态系统,简而言之是WebAPI。注1:一般来说,你可以用任何编程语言建立一个网站,还记得基于C++的OkCupid吗?,Co-star,都是用Haskell写的。如果你擅长其他编程语言,或者能招到擅长这些编程语言的工程师,你也可以成功。注意事项2:我正在尝试构建一个CRUD密集型Web应用程序API。它可能不是真正的网络“服务”——主要是快速地做同样的事情,无数次——而是一个网络“应用程序”——做很多不同的事情,其中??有相当多的业务逻辑。如果您正在构建与我正在做的不同的东西,那么我的建议可能不适合您。如果您需要快速执行一两个操作,例如您正在编写支付网关或语音消息应用程序,那么Rust可能工作得很好。警告3:这篇文章写于2021年1月,如果社区继续发展,Rust将继续改进并变得更好、更容易用于Web应用程序开发。总而言之,我真的很喜欢使用Rust,它是一种美丽的编程语言,有很多很酷的想法。希望很快,Rust将成为构建我想做的事情的最合适的工具。然而,我这几天想做的很多事情,在不同特性的编程语言中会工作得更好。