“最近 Rust in Production Podcast: Arroyo[1] 播客节目邀请到 Arroyo 公司的 Micah Wylde,来分享他们如何用 Rust 简化数据工程师处理数据流的方式。本文是对访谈内容的翻译,译者是 ChatGPT-4。
背景介绍
arroyo[2] 是一个 Rust 实现的分布式流处理引擎,旨在高效地对数据流进行有状态计算。与传统的批处理不同,流处理引擎可以处理有界和无界的数据源,并在结果可用时立即输出。
与许多现有的流引擎,包括 Apache Flink、Spark Streaming 和 Kafka Streams 不同的地方在于:
- 无服务器操作:Arroyo 管道被设计为在现代云环境中运行,支持无缝扩展、恢复和重新调度
- 高性能 SQL:SQL 是一项重要的关注点,始终具有出色的性能。使用 SQL 进行流的转换、过滤、聚合和连接,结果在亚秒级内返回。
- 专为非专家设计:Arroyo将管道API与其内部实现清晰地分离。构建实时数据管道不需要成为流媒体专家。
Arroyo 与 Kafka、Pulsar、Redpanda、Websockets 和 Server-Sent Events有良好的连接,结果可以写入许多接收系统,包括像 Kafka 和 Pulsar这样的流式系统,像S3这样的对象存储系统,以及像 Postgres 和 MySQL 这样的数据库。
更多介绍可以参考 Arroyo 文档[3]。
Micah Wylde 和 Arroyo 介绍
Micah Wylde 是 Rust 语言工程师,也是 Arroyo 流式处理引擎的创始人。Arroyo 是一个实时数据处理引擎,它让数据工程师可以在流数据上,利用 Rust 语言编写的用户自定义函数来执行 SQL 查询。
举个例子,就像处理存储在 Kafka 或其他流媒体系统中的数据。我在 Splunk 和 Lyft(一家美国的拼车服务公司)领导流数据处理团队五年后,开始着手这个项目和创办公司。更广泛地说,我几乎在整个职业生涯中都致力于大数据领域,从广告技术开始,处理实时广告竞价系统,再到领导数据团队和开发数据系统。
在 Splunk,我曾是主要工程师,也负责流计算团队,可以说在流数据处理方面颇有造诣。对于不太了解这个领域的人来说,我可以简单介绍一下流处理的概念。传统上,数据处理是以批处理模式进行的,意味着我们会从各种数据源收集数据,无论是日志、API 请求还是其他形式,这些数据最终会进入数据库或现在的数据湖、数据仓库。
一旦所有数据都准备就绪,我们就会在这些静态数据上进行大规模的数据处理作业。这通常意味着你需要等待一个小时甚至一天时间,直到所有数据到位,才能开始分析或了解相关信息。而流处理则不同,它是在数据实际进入系统的同时进行处理,也就是实时进行。显然,这种方式的优势在于其低延迟性。你可以在几毫秒或几秒内处理数据,而不是等待数小时或数天。
这不仅提高了效率,而且为构建端到端的数据系统提供了更为简便的方式。在构建高级分析或数据产品时,你需要考虑及时性和数据完整性等因素。对于像 Lyft 这样的实时服务公司来说,这种能力至关重要,它能让公司快速了解周边环境。比如,在拼车行业,了解用户和司机的位置、掌握交通速度对于路线规划非常关键。
流处理对于像 Lyft 这样的实时公司的重要性
对于像 Lyft 这样的公司来说,根据市场供需实时调整价格是必不可少的。所有这些工作都要求能够快速而复杂地分析数据,而不是等待一整天让数据全部汇入数据仓库。这就是流处理要解决的核心问题,以及它如何适应这种需求。
流处理技术并不是新鲜事物。过去已有其他公司在这方面做了大量的基础工作。你之前提到了 Hadoop 和 BigQuery,这些都是我在开创性文章中提到的技术,我们稍后会详细讨论。
但现在我想简要说明一下,为什么 Arroyo 在这方面有其特别之处,以及目前其他竞争者缺乏的是什么,这可能正是 Arroyo 的机会所在。BigQuery 和 Hadoop 都属于那种让数据积累起来,然后再对静态数据进行大规模处理的批处理模式。而在流数据领域,传统上最受欢迎的系统是 Apache Flink,它已经有十年的历史了。Flink 是第一个为流处理找到合适编程模型的系统,并且在正确性方面取得了突破,使其能够解决 Flink 之前许多系统无法处理的问题。在 Flink 出现之前,我们只有一些非常简单的系统,它们几乎不能保证任何正确性或完整性,更多的是围绕你自己的逻辑进行一些简单的编排。
在我的职业生涯中,我一直在 Flink 上工作。我认为,大多数现在正在进行创新的人也有类似的经历。对我们所有人来说,Flink 在为那些能够投入大量精力成为 Flink 专家的公司解决问题方面做得非常好。我曾经工作过的公司,需要组建由 10 到 30 人组成的团队,他们专门从事 Flink 的工作,围绕它建立基础设施和工具,特别是支持那些真正构建流数据管道的终端用户。
虽然 Flink 在帮助先进的公司推广这项技术方面取得了成功,使得几年前难以实现的事情变得可能,但它从未真正达到易用的程度,以至于可以让数据科学家、数据工程师或产品工程师独立构建实时数据管道。我们总是需要公司内部的 Flink 专家提供大量的实际支持。这正是我们在 Arroyo 中努力创新的方向。
我们正在努力构建一个系统,它足够简单,任何工程师或数据科学家都可以轻松上手,构建正确、可靠、高性能的实时数据管道。
流处理与像 Windmill 这样的工作流引擎之间的关系
问:那么,你如何看待流处理与如今兴起的新工作流引擎(例如用 Rust 编写的 Windmill[4] )之间的关系?你认为这两者之间有重叠吗?还是认为这些是完全不同的专业领域?
答:是的,我认为流处理和工作流引擎是截然不同的系统,它们各自擅长处理不同类型的问题。工作流引擎非常适合处理那些长时间运行的任务,比如需要基于一些简单规则,在一整天内完成一系列工作。例如,用户注册后需要发送一封电子邮件,根据他们的响应,我们可能需要执行另一连串操作。而这正是像 Flink 或 Arroyo 这样的流处理引擎不太擅长的领域。
要在这些不同的状态下制定这种类型的逻辑或条件逻辑是相当困难的。从架构上讲,使用这些强大的流处理系统来执行这类任务似乎有些过分。我认为,这些系统实际上可以很好地相互协作。流处理系统非常擅长解决以数据为中心的问题。这通常意味着你会把大量数据,例如每秒数百万个事件,输入到流处理引擎中。而这些引擎处理后产生的特征或事件,可以被规模较小的工作流系统所使用。
这实际上是一个常见的模式,让这些系统能够协同工作。但至少在近期内,我不认为它们会处于同一个领域。
实时 SQL 及其扩展的必要性
问:在你的网站上,你展示了一个很好的例子,你使用 Kafka 流,然后用 SQL(可能是 Apache Arrow 或类似语法,但它类似于 SQL)来通过你的系统处理事件,并实时查看结果,这是一个非常令人印象深刻的演示。所以 Apache Arrow 是类似于 SQL 的,还是有所不同?如果不同,那又是在哪些方面不同呢?所以,你主要是通过 SQL 来编写 Arroyo 程序的。
答:我们有自己的一些方言,但我们的目标是尽可能与 Postgres 兼容。要进行实时 SQL 处理,你确实需要以某种方式对其进行扩展。这里有不同的方法可以做到。但原本定义的 SQL 实际上是为批量计算而设计的,例如进行分组、聚合或连接。你需要所有的数据都准备就绪。否则,你怎么知道,在进行连接操作时,未来可能还会有更多数据加入,所以你就无法返回最终结果。
不同使用 SQL 的流处理系统都找到了自己的方法,来决定何时能够完成处理并为特定的表达式返回结果。在 Arroyo 中,我们采用了基于时间的窗口函数,如滚动窗口、滑动窗口和会话窗口。这些函数依赖于一种称为水位标(watermarking)的概念,这是一种基于估计的完成度概念。水位标是一种特殊的值,它会在数据流管道中流动,并告诉所有操作符,我们已经看到了或者我们认为已经看到了某个特定时间之前的所有数据。
这意味着,如果我们有一个在时间 t 结束的窗口,而我们收到了一个在 t 之后的水位标,这就告诉我们可以关闭那个窗口了,因为我们已经看到了该窗口内的所有数据,并且可以处理并将结果返回给用户。这是像 Flink 和 Arroyo 这样的流处理器中常见的模式。还有其他方法,像 Arroyo 和 Materialize 这样的系统也支持基于更增量式的计算方式,我们实际上是认为我们永远不会完成。
我们永远不会知道我们是否获取了特定时间段的全部数据。因此,每次有事件进入时,我们都会更新该窗口的状态,并在那里发布新的结果。因此,根据问题的类型,你可能会偏好一种 SQL 风格或另一种。无论如何,这都是 SQL。
数据系统编程语言的历史演变
问:你写了一篇名为《Rust 是数据基础设施的最佳语言》[5] 的文章,这是一个很有吸引力的标题。我读了这篇文章,我想知道,当你开始时,Rust 是否是你的首选?你是否也考虑过之前的解决方案?
还有,那时候 Zig 是否也变得流行了?你认为自己在这个领域的位置是什么?Rust 在正确的时间出现了吗?或者你会说,可能存在一种不同的现实,比如 Arroyo 是用 C 或者在不同的世界中用 Zig 编写的。
答:从历史上看,这类系统的最早版本,比如 Google 最初的系统,它们确立了我们今天对大数据的很多看法,如 MapReduce、BigTable 和 GFS,它们都是用 C 编写的。
接着,我们经历了一段主要使用 Java 编写数据系统的时期,例如 Hadoop 和 HBase。Flink 最初是用 Scala 编写的,后来转为 Java。随后,我们开始使用 Go 语言,比如开发 CockroachDB 和其他一些大数据系统。是的,我认为现在我们肯定不会为 Arroyo 选择 Java 或 Go。从很多方面来看,当前的系统开发是对之前使用 Java 编写系统的一种反思。许多人发现,通过用像 C 或 Rust 这样的非托管语言重新编写这些系统,可以显著提升性能并简化操作。
因此,我们在追随像 Red Panda(对 Kafka 的再实现)和 ScyllaDB(对 Cassandra 的再实现)这样的项目的脚步。我认为我们可以用 Java 或 Go 完成我们正在尝试的一些事情,但要实现我们的目标将会更加困难。**如果没有 Rust,我认为我们可能最终会选择 C **。但我非常庆幸我们生活在一个有 Rust 的世界。这无疑比如果我们必须选择 C 来开发会让我们的工作变得更加轻松。
特别是想为了优化,你需要尽量减少数据复制,在 C 中传递引用有时可能会非常棘手,尤其是如果你不完全了解你在做什么。即便你了解,有时也会遇到问题。
在 C 中传递引用的挑战
问:我很好奇,你的代码中是否也有很多与生命周期相关的部分,或者这是 Rust 编译器完全处理掉的东西,以至于你根本不需要考虑生命周期问题。
答:我们系统中最关注内存或生命周期的部分是存储层。
让我给出一些架构上的见解。这些系统看起来像是数据流的有向无环图(DAG)。你拿一个 SQL 语句,编译成 SQL 计划,然后最终将其优化成这个数据流图。图中的每个节点都是某种可能具有状态的操作符。例如,执行过滤或映射,或者是像窗口或连接这样的有状态函数。在这些操作符之间,事件和处理后的数据通过队列或网络套接字传输。
在这些有状态的操作符中,我们可能需要长时间存储数据。比如,如果你有一个 30 天的滑动窗口,我们需要存储那些数据的某种表示,持续 30 天。我们通过离线 S3 存储、本地磁盘缓存和内存缓存的组合来实现这一点。管理这个内存缓存就涉及到生命周期的问题,即如何管理数据从缓存流向处理器以供使用。幸运的是,这些系统的架构在一定程度上限制了这个问题。
在语义层面上,你在这些操作符中一次只处理一个事件。因此,你不需要直接处理层面的真正并发问题。这最终简化了你可能在更传统的数据库中遇到的生命周期管理问题,那里你可能面临对同一数据的多个不同请求。
Rust 和 Lingua:类型和并发
问:因此,在 Rust 的术语中,这意味着你的类型不需要是同步的,或者它们不必是同步的?
答:对。从逻辑上来看,这些操作符中的每一个都是单线程的。这一切都是通过 tokio 实现的,所以实际上背后的情况要比这更复杂。但作为开发者,你真的可以把它想象成同步处理,而不是单线程的。说到 tokio,它似乎非常适合这种用例,因为你正在利用那些本质上是并发的东西,它们不一定非得按顺序执行,至少它们的某些部分可以同时并发执行,有时甚至可以并行执行。
问:但我很好奇你对 tokio 的看法,你使用这个框架的经验,它的易用性如何,以及最近关于异步 Rust 是 Send/Sync
等的讨论,工作窃取、调度器等等。
答:是的,从高层来看,像 Arroyo 这样的系统实际上不需要像 tokio 这样复杂的调度器。正如我提到的,这些操作符基本上像单线程一样运行。它接收一个事件,处理该事件的所有内容,然后将其发送到下一个目的地。所有这些都必须发生,以维持系统的正确性保证。因此,Arroyo 的第一个版本实际上是围绕线程和线程处理构建的。在某个时刻,它转移到了 tokio 和 AsyncRust,实际上很早就开始了。
核心原因是,目前生态系统中有很多 AsyncRust,如果你想使用常见的网络库、数据库驱动程序或几乎任何来自网络编程生态系统的东西,你在某个时候必须处理异步。在某个时候,将整个系统转移到异步变得更容易。这无疑是一个具有挑战性的迁移。实际上,对我来说,我之前从未使用过异步 Rust。所以这涉及到很多学习,很多时间在 tokio Discord 频道上,这是非常有帮助的。
但最终,令人惊讶的是,它实际上变得更快了。仅仅进行这次迁移使系统快了大约 30%,这完全出乎我的意料。但事实证明,tokio 调度器在这类问题上非常有效,即使从高层来看,所有这些处理看起来像是单线程的,但实际上背后有很多更多的事情需要协调。比如,你实际上有线程,比如在我们的案例中,与 S3 或其他系统通信。
我们有很多队列参与其中。所以即使我们只有少量的实际处理线程,还有很多网络交换在其他线程上发生,比如通过 gRPC 进行协调。tokio 在有效组织所有这些工作方面表现出色,真正充分利用了你的核心资源。对我们来说最令人惊讶的是,我们能够以极高的利用率运行系统,如超过 95% 的 CPU 利用率,一切仍然保持响应和反应,能够在这种极高的 CPU 使用率下高效工作,这在我使用其他编程范式编写的系统中从未有过的体验。
就我对异步 Rust 的看法而言,我想,如果我们要用“戏剧性(drama)”这个词,我认为 Rust 社区总体上有更多的戏剧性,我不完全理解为什么。但我认为也许这项技术运作得太好了,以至于我们不得不发明其他东西来发泄不满,但我会说异步 Rust 确实有学习曲线。对我来说,从一个我认为已经相当强大的 Rust 程序员转变为有效的异步 Rust 程序员,花了大约一个月的时间,这绝对是其他人对其做出贡献时遇到最多麻烦的系统边缘,这在你不熟悉处理异步并发相关策略的情况下确实会令人沮丧。
有时,编译器中糟糕的错误消息也不会有所帮助。这可能使你很难弄清楚在大量代码中问题到底是在哪里引入的。但我要说的是,总体上,tokio 对我们来说是一个巨大的福音。它真的很了不起,让我们几乎不用考虑如何安排工作。它自己就做得非常好。Rust 可以保护你免受内存或安全问题,但它不能保护你免受竞争条件。
问:所以我想知道,作为一个大规模使用 Rust 和 tokio 的人,你是否遇到过任何数据竞争或在运行时遇到的问题,这可能对你的平台构成了一些问题,或者你从未在生产中遇到过任何故障?
答:不是特别来自竞争条件的问题,正如我提到的,我们系统的架构使得高层次的并发相当直接,尽管在细节中有更多的复杂性,尤其是当你试图达到下一个性能水平时。例如,存储系统非常复杂,具有很多并发性。
但 Rust 确实在管理这种复杂性方面帮了很大的忙。在生产中遇到的问题更多的是关于高层次的问题,即这个分布式系统的不同部分如何相互作用,以及不同部分对其他部分正在做的事情的错误假设。不幸的是,Rust 绝对无法解决分布式系统的问题,但在微观层面上,一旦你让它们编译,事情的运作就非常出色。
我曾经在大约两个连续的 12 小时工作日里直接进行了编码,最后花了大约一个小时来尝试编译它。然后,它在第一次编译通过后,就完美运行了。我创建了一个单节点系统,一个分布式系统,没有经过任何测试,也没有进行迭代。自从最初实施以来,它基本上没有发生变化。我以前从未在 C 或者甚至 Java 中编写网络软件时经历过这种情况。
Rust 中的平稳编译和卓越性能
问:这确实令人印象深刻,确实是。非常了不起,你能够做到这一点。这也证明了Rust 的类型系统的有效性,以及借用检查器以及所有使 Rust 开发和开发人员体验变得非常出色的元素。不过,我想知道,尽管你可能没有太多运行时问题,但是否在编译时遇到了任何问题,比如生态系统的某些部分不一致,例如与不同版本的 tokio 的兼容性问题,或者不同的库有时成熟度不同。
答:是的,这从来没有成为一个大问题。总体来说,Rust 所创造的生态系统对我们来说非常有帮助,从生产力的角度来看,与 C 世界相比,使用依赖项非常具有挑战性。在 C 世界中,使用依赖项非常具有挑战性。而在 Rust 中,我们已经拥有了一个令人难以置信的丰富生态系统,而且这是在相对较短的时间内实现的。所以偶尔会出现兼容性问题,我们不得不分叉一些我们依赖的开源项目,但我要说的是,处理这些问题在我的日常工作中所占的比例非常小。
问:你提到的所有东西对于一般开发人员来说都是相当低级的,或者至少需要对如何构建这样的系统或架构这样的系统具有很多专业知识才能表现得很好。我想知道,你认为Rust有多大程度上指导你朝着符合惯例的解决方案前进?以及你自己的专业知识是什么?
答:是的,我认为 Rust 确实指导你朝着正确的解决方案前进。不过,它可能并不总是帮助你变得符合惯例。尽管周围的工具非常有帮助,比如 Cargo Clippy 非常有帮助,一直以来都很有帮助。
所以,我的联合创始人以前从未使用过 Rust,在这个项目上工作之前。他是一位非常有经验的分布式系统工程师,曾经参与过很多查询系统的开发,但对 Rust 还不太熟悉。像 Clippy 这样的工具确实帮助他很快地掌握了 Rust 的惯用风格。此外,我认为 Rust 社区也非常有帮助,我已经提到了 tokio Discord,在我尝试迅速了解AsyncRust 时非常有用。但总的来说,Rust 社区非常有帮助,可以帮助你解决问题,或者搞清楚为什么会出现一些奇怪的编译问题。
问:在开始学习 Rust 时,除了官方的 Rust Book 和社区之外,您是否使用了其他资源?还是您在项目上开始学习的?
答:实际上,我从 2014 年开始使用 Rust,但直到现在,我从未说服公司在 Rust 中进行重大项目。在大型组织中引入 Rust 总是一项艰巨的任务。但在很长一段时间里,我一直在我的个人项目中使用它。我自从第一次了解它以来,一直都是这种语言的粉丝。
但就我自己的发展而言,这段时间有很多资源。Rust Book 的第一版对我非常有帮助,我甚至在书架上放着它。不过,在早期阶段,Rust 发生了很大的变化,以至于跟踪语言的更新几乎成了一项全职工作。如今情况好多了,Rust 已经相对稳定了几年。我认为资源的质量和数量也有了很大的提升。但我知道有一本关于 Rust 在生产环境中的非常好的书,我经常查看它,它更多地涵盖了一些具体的问题,比如在Rust 中如何记录日志,如何进行度量等,这些问题不一定是初学者级别的内容。这本书叫做《Rust in Production》,作者是 Luca Palmieri。
问:你提到有时很难说服大公司和组织转向 Rust 并在这些公司引入 Rust。根据你的经验,这是为什么呢?
答:是的,我认为大公司往往不太愿意在技术选择上采取大胆的举措。很多时候,他们更注重最小化风险,而不是最大化回报。对于今天的 CTO 来说,Rust 似乎是一种冒险。
问:他们担心工程师学习如何使用Rust会太困难吗?如果我们重新组建团队,是否能够将这个项目移交给另一个团队?他们是否需要弄清楚如何使用它?我们是否能够雇佣足够多的Rust工程师?
答:如果你是 Google,需要雇佣 10,000 名工程师,我认为你确实应该担心是否能够雇佣到足够多的 Rust 工程师。我怀疑世界上是否有那么多的 Rust 工程师。但对于规模较小的公司来说,这不是问题,对吧?雇佣三名 Rust 工程师非常容易。而且我认为,尤其对于小公司来说,使用 Rust 可能是一种优势,而对于大公司来说可能并不是。
因为作为一家小公司,你可以吸引那些想要用 Rust 工作的人。这对于吸引人才来说是一个巨大的激励因素。我认为,即使在使用可能稍微晦涩的语言工作时,你也会吸引那些真正对它感到兴奋的人。这对你来说可能是一个巨大的好处。但对于大公司来说,他们更多地看到了其中的风险。
Rust对小公司的优势和招聘Rust工程师
问:如何雇佣 Rust 工程师?你是通过人际网络联系他们,还是在某个地方发布招聘广告?
答:是的,嗯,实际上对于我们来说,最初我们更多地是在流媒体专业方面进行招聘。现在与大约两年前相比,这两个领域可能有更多的重叠。许多较新的流媒体系统也使用 Rust。但从历史上看,正如我之前提到的,流媒体系统主要是使用 Java 开发的。所以大多数人都有 Java 方面的专业知识。但我确实预计,随着我们尝试更广泛地招聘人才,从 Rust 工程师的人才库中招聘将会非常有成效,尤其对于非加密货币的 Rust 公司来说,目前对这些职位的需求非常大。所以我们将能够从中获益。
问:非常正确。您使用哪些第三方 crate 来完成工作呢?我想在博客文章中,您提到了 Data Fusion。也许这是您可以谈论的一个crate,但随时可以谈论您喜欢的任何其他 crate。
答:是的,Data Fusion 对我们来说可能是最关键的一个。Data Fusion 包括了多个方面,它源自于 arrow-rs 生态系统。我们主要将其用作 SQL 解析器。它将 SQL 文本转换为AST(抽象语法树),然后生成计划(planner)。通过将 AST 转换为描述SQL 应该执行的面向图的计划。
SQL 是一种极其复杂的语言,有着 30 年的历史和许多不同的等效表达方式。因此,当构建 SQL 引擎时,拥有一个处理许多复杂性的库对你来说非常有帮助。我们从中得到了一个清晰的计划,然后可以按照我们自己的方式进行优化并编译成一组我们自己的操作符。因此,Data Fusion 对我们能够如此迅速地构建这个系统非常关键。此外,我还想提一下可能更低级或更高级的 Rust Web 生态系统,实际上非常感激这一点。
所以我们依赖于 Axum 和 SQLX,这是一个非常出色的 SQL 库。这完全不像我们产品的核心部分。这主要用于支持我们的 API 和 Web 界面。但令人惊讶的是,即使在Rust可能不太适用的领域,我们仍然有这些极高质量的库,使构建优质产品变得非常容易。这对我们来说是一个令人印象深刻的发现。你提到的这些库,我不能说太多关于 Data Fusion,但绝对可以肯定其他的库在任何语言中都是一流的,至少根据我的经验,我以前使用过 Axum 和 SQLX,我认为它们真的非常出色。
令人印象深刻的 Rust Web 生态系统和高质量的库
问:但我想知道这个生态系统的未来如何。你是否认为我们已经达到了一个点,crate 开始更多或更少地稳定下来,有一个主要的 crate 可以用于你的工作?还是你认为这个生态系统仍然很年轻,如果有其他的web框架或解析器等新的工具出现,你可能在一年后或更长时间内会考虑切换?
答:是的,我认为现在说事情已经稳定下来可能还为时过早。一年前,你在许多领域的选择可能已经不同了。确切地说,三年前,这些 crate 都不存在。即使 Axum 本身也在不断变化,每个版本都有很大的改进。所以我认为,即使这些 crate 本身还没有完全稳定下来,但我认为我们将会迎来更多的稳定时期,尤其是随着异步 Rust 变得更加功能完善。许多这些库不得不在异步生态系统和实现的限制方面进行调整,比如缺少使用异步函数和特性的能力,这个功能已经实现或即将实现。
我认为这将允许这些库以迄今为止一直具有挑战性的方式稳定它们的API。我期待未来会有更多关于使用哪个 crate 来解决不同问题的明确选择,而且更多的稳定性。对我来说,Rust 生态系统的令人印象深刻之处在于,或许有机会早些时候稳定下来。只是随便举个例子,对于日志记录,我们曾经有 log crate,它在很长一段时间里是记录日志的明显选择。
但实际上,事实证明有更好的选择和更好的设计。最终,我们选择了 tracing
crate。生态系统能够迁移到这个更好的选择,而不会陷入局部最优的困境。在许多不同领域,我们都看到了这种情况,早期有关某个 crate 作为解决这一类问题的方案的共识,但生态系统能够发展到更好的解决方案上。
我认为这不是所有生态系统都具备的特性。我非常欣赏 Rust 社区能够相对迅速地、并且以一种相对共识驱动的方式转向生态系统中的更好选择。所以我认为我们将继续看到这种情况发生。至于 Axum 是否是 Rust Web 编程的最终解决方案,我不知道。我认为我们将继续看到迭代的发生。
Rust 核心稳定性和标准库的期望
问:那么 Rust 语言自身,标准库呢?Rust核心的稳定性如何?
答:我认为每个人都有自己的 RFC 愿望清单,希望最终能够合并。我个人认为,对于我来说,关于 async 的完整性不足 一直是最大的困扰。例如,缺少 async fn in trait,对我们来说需要很多有些笨拙的变通方法。而且即将稳定的版本对于我们所有的用例来说还不够完整。
但我欣赏 Rust 花时间来解决这些问题。我认为我们已经看到了在 async 方面的这个过程。总的来说,Rust 编程语言目前处于一个非常良好的状态。与前五年相比,它已经稳定下来了。我们将继续看到稳定化,希望会有一些不错的改进,比如 从GADTs 得到的工作或者现在看到的 async 的改进。
问:我认为在 Rust 与其他语言进行 FFI(外部函数接口)交互或者在运行时加载代码时,存在问题。我想对于一个流媒体平台来说,这也是一个有趣的用例,也许你可以在运行时将一些东西连接到你的引擎上。当然,还有像 WebAssembly 这样的技术在不断发展。我想知道你是否已经尝试过这个,以及你对当前环境的生态系统有什么看法?
答:是的,事实上,也许我应该在我的博客文章中提到这一点,我在博客文章中称之为一个困扰。
Rust 没有稳定的 ABI(应用程序二进制接口),这对于构建类似于插件系统的任何东西都是具有挑战性的。所以如果你想编译,就像我们支持用户定义的函数,所以用户可以编写 Rust 代码,然后在运行时加载到引擎中。如果你用 C 或 C 编写这个,有一个稳定的 C API,可以在运行时动态链接软件。Rust 没有这个功能。所以如果你想编译一个库和一个主机应用程序,并将它们链接起来,你必须使用完全相同版本的 Rust 编译器。
而且在许多情况下,还需要相同的编译器设置。所以这使得从使用该库的事物中分发二进制软件变得非常困难。所以今天,你基本上不得不使用C API,这意味着在接口上放弃了很多 Rust 的功能和能力,至少在接口上是这样。你还提到了 Wasm,这是解决这个问题的另一类解决方案。在某些方面,从接口的角度来看,这甚至更糟糕。因为在 Wasm 生态系统中,没有真正的标准方式来在主机和插件之间进行交互。
所以每个应用程序都必须自己解决这个问题。我们已经探讨过Wasm作为解决这类问题的一种解决方案。实际上,我们已经与 Wasmtime 进行了集成,这是一个出色的Rust Wasm 运行时。对于像我们这样的系统来说,这可能会是今后的方向。特别适用于与其他语言生态系统集成,并且在 Wasm 领域有很多精力来解决这些集成问题,例如 Rust 程序如何通过共享的 Wasm 内存与 Python 程序进行通信,以及如何构建这种统一的接口,这意味着个别项目像我们这样的不必一次又一次地解决这类问题,但如果 Rust 在与其他编译代码动态交互方面更加出色的话,那就太好了。
你知道有没有任何 RFC 提出了 Rust ABI 的稳定化?是的,这个领域有几个不同方法的 RFCs,但在过去几年里,我没有看到太多的进展,也没有真正的兴趣来解决这个问题。我认为这在某种程度上是一种小众需求。大多数 Rust 项目都以源代码的形式分发。大多数库在构建时编译。但对于像我们这样的项目,或者任何涉及到插件生态系统的项目,这已经远远不够了。我想我们已经涵盖了项目的很多技术细节。
问:我也想谈谈一些与业务更相关的事情。我想,首先要问的问题是,据我所知,Arroyo 由Y Combinator 支持。投资者是否曾经关心过你选择的编程语言,或者从未有过讨论?或者甚至可能是一件好事,也许他们鼓励你使用 Rust。
答:是的,Rust 有助于我们与投资者交流。已经有一些系统在我们之前证明了 Rust 可以在商业上发挥作用。
投资者知道它是数据领域的热门语言。所以如果你使用 Rust,在这个意义上,你看起来肯定更有吸引力。但老实说,大多数投资者不关心你选择的编程语言。他们不在那个层面上操作。如果你作为这个领域的专家,并且你说,我认为这是正确的技术选择,投资者不会对此产生怀疑,他们更关心的是商业问题,比如你将如何销售这个产品,谁将成为你的用户,为什么他们会选择你而不是这个领域的更成熟的公司,他们绝对不会询问你为什么使用 tokio 或 async-io 等等。
对于那些处于同一领域,正在考虑使用 Rust 的人,可能已经涉足其中,但不确定是否应该为他们的下一个项目全身心投入其中,你会有什么建议?嗯,我认为在这个领域,Rust 只是当今的明显选择。你知道,我们经历了用 Java 或 Go 等构建这些系统的整个时代。
但今天,特别是在当前的宏观经济环境下,公司更加注重成本。当你可以用 Rust 编写一些东西,它的资源占用只有 Java 版本的一半或四分之一时,这是一个巨大的、巨大的卖点。Java 系统正在重写一些核心部分,比如我们看到 Spark 正在用 C 重写他们的核心引擎。Confluent,Kafka 的人也一直在重写一些东西,所以我认为如果你不使用 C 或 Rust,要竞争就真的很困难。
即使今天可能有更多的 C 开发人员,教一个人成为一个优秀的 Rust 程序员要比教他们成为一个优秀的 C 程序员容易得多。而且 Rust 编译器对于不太熟悉内存管理的人来说非常有帮助。这使得更难出现这些类别的错误。所以我认为这是非常明显的选择。也许还有一些较新的语言可以尝试。你之前提到了 Zig。但所有这些所谓的 Rust 替代品,都没有 Rust 成熟,所以如果你想要尝试,你真的必须非常有勇气。所以,我认为要么是 C ,要么是 Rust,而且除非你有充分的理由使用 C ,否则我认为 Rust 是默认的选择。
问:以 Confluent 为例,他们确实对其代码库的部分进行了重写,采用了 C ,如果我理解正确的话。我想知道为什么他们选择了 C 而不是 Rust,因为 Rust 可能已经是一个非常成熟的替代方案了。为什么他们没有选择Rust呢?或者是因为Rust在那时还没有那么成熟吗?
答:是的,也许我可以说说 Spark 的情况。我对这方面可能有更多的背景信息......但是,Spark 的历史是用 Scala 编写的,后来主要是用 Java 编写。然后 Databricks 将他们的核心引擎重写成了 C 。
而且他们一直保持了闭源。我想这个项目大约是在六年前开始的,那时的 Rust 不如今天成熟。
问:你知道在这个领域还有其他公司计划重写他们代码库的部分吗?
答:一个很好的例子就是 InfluxDB,最初是用 Java 编写的,然后他们重写成了 Go,但最近刚刚采用了 Rust 完成了他们的核心存储引擎的重大改写。实际上,我们也从中受益匪浅,因为他们非常支持 Data Fusion 和 Arrow 项目。
另一个例子是 TiKV,他们最初使用 Go 编写,然后重写了他们的核心引擎,采用了Rust。所以我认为,这在最近几年已经成为一个相当普遍的趋势。
问:展望未来,也许在未来三四五年里,看看可能在途中启动的项目和正在发展的事物,你对未来有什么看法,你看到这个行业的走向在哪里。
答:是的,我可能说了很多遍,对于那些要启动新的数据系统或新的大规模系统的人来说,未来大多数人都会选择 Rust。还有一些人在启动新的 C 系统,但只看我自己的领域,四分之三的新系统都是 Rust,四分之一的新系统都是 C 。我认为随着 Rust 在技术和招聘方面会变得更加容易。
也许我们会看到这些其他新的语言能够变得更加成熟,开始吸引项目。在某个时候,我相信 Rust 会变得乏味,人们会想使用更令人兴奋的语言。但对于 Rust 项目来说,我认为这将是一个非常成功的结果。目前为止,我们对技术选择一点都没有后悔。我们已经进行了一年多的时间,Rust 已经证明是一个非常成功的技术选择。
在基础设施与应用软件中采用Rust的情况
因此,在基础软件和应用软件之间存在一种区别。基础软件,就像我们正在开发的东西,或者例如数据库,通常由一个小团队编写,然后由更大的一群人运行。因此,确实有道理......付出很多努力使其真正高效和快速,因为它将在其生命周期内运行在许多 CPU 核心上。对于应用软件来说,开发成本与运行时成本差距不大,甚至可能更大,因此您不一定有同样的财务压力要使其非常高效。
而今天,我认为在这个领域,Rust 更难以推广,因为使用 Rust 编写代码和雇佣或培训 Rust 工程师会增加额外的复杂性。因此,我想知道,Rust是否会在这个领域通过语言和生态系统的成熟以及愿意使用 Rust 或希望使用 Rust 的人的不断增加而继续发展。对我来说,这是一个有待开发的领域,可以让一门语言或一门新语言进入并占据这个领域,因为我们可以在应用级别的编程方面做得比 Java 和 Go 更好。
Rust 的许多人性化特性非常适合这种情况,但处理 Rust 在内存管理和生命周期问题方面的一些棱角,如果您不太关心性能,就不应该为那些问题而烦恼。
如果看看与之相关的数据科学领域,似乎他们也开始尝试一些来自Rust世界的想法,甚至可能会重写他们的库的某些部分以在性能较低但更高级别的语言(如Python)中使用它。
问:你有 Parquet (列式格式)文件,然后围绕它进行解析,还有 pandas 等等,对于 Rust 来说,这是一个有趣的领域,因为它是性能与数据分析的混合体,性能也是相关的,对吗?您是否同意这一观点?
答:是的,我认为这绝对会持续存在。这实际上是将 Rust 核心包装在高级语言(如Python)中的一种方法。这已经取得了非常非常成功的效果。在 Java 生态系统中,我们也看到了这一点,许多 Java 工具已经在 Rust 中重写了它们的核心,并获得了10 倍甚至更多的性能提升。
我个人不是 Python 的爱好者,但显然人们非常喜欢它。很难说服数据科学家使用除Python 以外的任何东西。因此,如果您想提供更好的性能,我认为这种在 Rust 中编写核心代码然后将其包装在高级语言中的方法已经取得了非常成功的效果。
Mojo : 像 Python 又有 Cpp 性能的语言
问:我猜还有另一种令人着迷的方法,不知道您是否熟悉 Mojo。这是克里斯·拉特纳(Chris Lattner)的新语言,他是 Swift 的创作者。这是创建一种类似 Python 的语言,实际上编译成 LLVM 和 MLIR ,旨在提供 C 级别的性能。
答:对我来说,这个类似 Python 的语言非常雄心勃勃,因为它具有某种程度的Python 兼容性。考虑到 Python 的语义以及优化的难度,我认为它是非常有雄心。但是,如果你不想采用这种 Rust 的方法,那么这似乎是唯一能够获得可接受性能的方式,尤其是在使用这些 Python API 的情况下。对于我们来说,因为我们从 SQL 开始,将 SQL 优化为任何所需的实现非常容易,这为我们提供了很多优势,以提供非常高性能,因为 SQL 是一种声明性语言,你可以以更快的方式重写表达式以实际执行。但是,当你使用类似 Python 的东西时,你的优化能力会受到很大限制,即使使用 Rust 核心也是如此。因此,我认为看到数据科学领域,随着数据量的增加和我们处理复杂性的增加,金融压力将如何推动人们采用高性能范式将是很有趣的。但是,目前我认为 Polar 的方法,即使用 Rust 核心,将在许多数据科学生态系统中看到。
问:你想给整个 Rust 社区一句什么建议?
答:我想让 Rust 社区要稍微冷静一点,Rust 是一门令人难以置信的语言,有一个令人难以置信的生态系统和社区。然而,我们似乎有比我所参与的任何其他语言社区多十倍的戏剧性事件。我不太明白为什么或者这些戏剧性事件的根源在哪里。但我认为这种程度的戏剧性只会损害 Rust 的采用率,当人们查看 Rust Reddit 并说“这是一场糟糕的秀”的时候,我为什么要加入这个社区?所以,我希望我们可以回顾过去的一年,只是说,我们都需要稍微冷静一点,弄清楚如何与其他人合作,停止驱赶人们离开社区。这是一个很棒的最终陈述,我真的很喜欢。
后记
Micah Wylde 分享了他对 Rust 以及 Rust 生态的看法,并且分享了为什么他认为 Rust 更适合数据基础设施的认知。希望这篇文章能对你有所帮助。
感谢阅读!
参考资料
[1]
Rust in Production Podcast: Arroyo: https://corrode.dev/podcast/s01e04-arroyo/
[2]
arroyo: https://github.com/ArroyoSystems/arroyo
[3]
Arroyo 文档: https://doc.arroyo.dev/introduction
[4]
Windmill: https://github.com/windmill-labs/windmill
[5]
《Rust 是数据基础设施的最佳语言》: https://www.arroyo.dev/blog/rust-for-data-infra