前言#
昨天我们见识了一些异步中的难点,知道了一些绕过它们的方案
今天我们继续往下
异步的生态(ecosystem)#
现在的rust仅提供了一些编写异步代码的“基本必需品”。比如执行器(executor)、任务(tasks)、响应器(reactors)、关系选择器(combinators)、低等级(low-level)I/O的futures以及traits现在标准库里都没有。
但是好在社区大佬多,上面这些问题基本上都有社区crate可以填补。
异步基础团队(The Async Foundations Team)对于拓展这本书(The Async Book)中涵盖多个runtime的例子比较感兴趣。
如果你对这个也感兴趣,你可以在这call和大佬们接触:
Async Runtimes#
async runtimes是一些用来执行异步应用的库。
runtimes一般包含一个reactor也就是响应器以及一个或多个执行器(executors)。
reactor提供订阅机(subscription mechanisms)给外部事件,比如异步I/O、进程间的交流(interprocess communication)以及timers定时器等。
在一个异步runtime中,订阅者们(subscribers)一般都是一些低等级I/O运算的future。
而执行器们(executors)处理调度(scheduling)并且执行任务(tasks)。它们与运行保持联系、悬挂任务、轮询futures推动完成以及唤醒任务当它们可以再次执行了。
executor也就是执行器经常被用来描述runtime,反之也是。在这里,我们用ecosystem也就是生态来描述一个runtime包含兼容性的traits和futures。
社区提供的一些异步crate#
Futures
这个我们之前就接触过了,比如join!/select!就是来自于这个crate。
它里面包含着一些方便编写异步代码的traits,比如Stream、Sink、AsyncRead以及AsyncWrite这几个traits还有一些实用工具比如combinators。这些实用工具和traits在将来有可能变成标准库里的一部分。
futures有它自己的executor,但是没有reactor,所以它不支持异步的I/O和timer。
因此,它不是一个完整的runtime。一般的选择都是使用futures里的工具但是使用别的执行器。
热门的Async Runtimes
标准库里没有async runtime,官方也没有推荐的,所以如果有需要那就得去社区里找。
下面这几个是比较热门的async runtime:
Tokio:一个热门的基于HTTP,gRPC(google Remove Procedure Call)[5][6]以及tracing frameworkfs跟踪性框架的异步生态。async-std:一个crate可以提供标准库组件(components)的异步对应项(asynchronous counterparts)。smol:一个小的简单的async runtime。它提供Async这个trait,这个trait可以用来包裹struct比如UnixStream或者TcpListener。fuchsia-async:一个执行器用于Fuchsia OS[7]中。

确定async生态系统的兼容性#
不是所有应用/框架/库之间都是兼容的,或者不支持某个平台/操作系统。
有以下部分异步框架/库要求某个特定的生态。
生态的约束并不总是有文档的,不过这里有一些经验规则可以帮我们确定一个库/trait或者函数是否依赖于某个特定的生态:
- 任何涉及到与异步
I/O/定时器/进程交流/任务的异步代码一般都依赖于某个特定的异步执行器/响应器。 - 其它异步代码比如异步表达式、组合选择器(
combinators)、同步类型、流(streams)一般都是生态独立的,有它们套着(nested)的future一般也都是生态独立的。 - 在开始创建一个新项目之前,先去搜一下你这个引用的
async runtime和别的库/框架是否有冲突,不然到时候要替换难度就大了。
注意,Tokio使用的是mio的响应器并且自己定义了一套async I/O traits的版本,包含AsyncRead和AsyncWrite,这俩来自于futures。
他自身依赖于async-executor这个crate,和async-std以及smol这俩冲突。
不过也不用太担心兼容性问题,一般异步生态都会给出兼容层(compatibility layers),这样你就能从一个runtime中调用另一个runtime的代码。比如async_compat这个crate提供了Tokio和其它async runtime的兼容层。
一般库提供的异步API都应该是独立的,除非需要创建任务或者定义他们自己的异步I/O以及定时器futures。
不过理论上应该由binary来负责任务的调度和运行。
单线程执行器 vs 多线程执行器#
异步执行器可以是单线程也可以是多线程的,比如async-executor这个crate就有单线程的LocalExecutor和多线程的Executor。
一个多线程执行器可以提供更多的任务同时运行,不过代价自然就是更多的开销。
所以在你选择多线程异步执行器之前,你需要确定你这个项目是否真有需要开多线程。
任务可以运行在创建它的线程里,也可以在独立线程里。async runtimes一般会提供派发任务到独立线程的基础功能。
即使任务是在独立线程里执行的,它们也应该保持non-blocking也就是非堵塞的。
当然,既然任务可以被调度到独立线程,那么它也得是实现了Send的。
一些runtimes提供函数用来生产non-Send的任务,这样就能确保每个任务不会跑去别的线程。
这些runtimes也可能顺便提供了生产堵塞型任务派发到独立线程,这样可以堵塞来自于其它库里同步的代码。
总结#
莫得总结,这样节主要就是介绍下异步生态等。
参考#
- ^the-async-ecosystem https://rust-lang.github.io/async-book/08_ecosystem/00_chapter.html#the-async-ecosystem
- ^Aasync-Runtimes https://rust-lang.github.io/async-book/08_ecosystem/00_chapter.html#async-runtimes
- ^community-provided-async-crates https://rust-lang.github.io/async-book/08_ecosystem/00_chapter.html#community-provided-async-crates
- ^futures-crate https://docs.rs/futures/
- ^聊一聊什么是gRPC https://zhuanlan.zhihu.com/p/363672930
- ^gRPC https://grpc.io/
- ^Fuchsia OS https://fuchsia.dev/
- ^determining-ecosystem-compatibility https://rust-lang.github.io/async-book/08_ecosystem/00_chapter.html#determining-ecosystem-compatibility
- ^single-thread vs multithreaded executor https://rust-lang.github.io/async-book/08_ecosystem/00_chapter.html#single-threaded-vs-multi-threaded-executors
发布于 2023-02-01 15:13・IP 属地广东
