辉煌的 2018 年,更美好的 2019 年

2018 年 12 月 19 日

一年前,Tokio 还是一个非常不同的库。它包括(现在已弃用的)tokio-core,该库在一个库中提供了 future 执行器、I/O 选择器和基本的 TCP/UDP 类型。它还包括 tokio-proto,但我们暂且不谈论它。在过去的一年中,Tokio 已经成长为 Rust 的异步 I/O 平台。它已被 许多 大型 公司 采用,以构建 应用

2018 年取得了许多成就。一些亮点包括

  • 引入了高性能、多线程、工作窃取调度器。
  • 计时器已从头开始重建。
  • 引入了文件系统 API。
  • UDS、TLS、信号和其他 API 已添加到 Tokio。
  • 最重要的是,Tokio 运行时作为构建异步应用程序的“开箱即用”平台被引入。

这还不包括无数较小的改进。例如,改进的文档、错误修复、性能提升和精细化的 API。这些改进由令人印象深刻的 165 位个人贡献者 贡献。

的确,2018 年对于 Tokio 来说是辉煌的一年,这一切都归功于您。话虽如此,我们才刚刚开始,2019 年有望变得更加美好。我想重点介绍一些预计在 2019 年推出的重大事项(已经在进行中)。

Async / await

Async / await 正在构建到 Rust 语言中。它使编写异步代码几乎就像编写同步代码一样。这项工作已经进行了一段时间,预计将在 2019 年的某个时候在 Rust 稳定版中发布。async / await 的含义是在使用 Tokio 时,人体工程学方面将有很大的改进。

如果您愿意使用 Rust nightly 编译器,您今天就可以将 async / await 与 Tokio 一起使用 今天。简而言之,依赖于带有 async-await-preview 功能的 tokio 并尝试一下。

这是一个尝鲜

pub fn main() {
    tokio::run_async(async {
        let client = Client::new();

        let uri = "http://httpbin.org/ip".parse().unwrap();

        let response = await!({
            client.get(uri)
                .timeout(Duration::from_secs(10))
        }).unwrap();

        println!("Response: {}", response.status());

        let mut body = response.into_body();

        while let Some(chunk) = await!(body.next()) {
            let chunk = chunk.unwrap();
            println!("chunk = {}", str::from_utf8(&chunk[..]).unwrap());
        }
    });
}

完整示例

那么,Tokio 完全采用 async/await 的路径是什么?让我们稍微谈谈这个问题。

首先,async/await 必须在 Rust 稳定版中发布。确切的目标日期尚不清楚,但预计将在 2019 年发生。一旦发生这种情况,Tokio 将立即以向后兼容的方式添加支持。实现此策略的方法目前正在通过实验性的 async-await-preview 功能标志进行探索。在较高层次上,async/await 特定的 API 将添加 _async 后缀。例如,tokio::run_async 将是使用 async fn(或 std::future::Future)启动 Tokio 运行时的途径。

一旦 async/await 支持成熟一段时间后,Tokio 将发布一个破坏性更改,并删除 _async 后缀。tokio::run 将默认接受 async 函数。

那么 futures 0.1 呢?我们不能立即放弃对 futures 0.1 的支持。有一个不断增长的生态系统,包括基于 Tokio 构建的生产应用程序,其中包括使用 futures 0.1。过渡需要时间。这将分多个步骤完成。

首先,以向后兼容的方式添加对 async/await 的支持。这增加了对 async/await futures 0.1 的同时支持。然后,async/await 成为主要的 API,futures 0.1 可以通过兼容层使用。这将使使用尚未更新到最新 Tokio 的库成为可能。

我们意识到对于已建立的生态系统来说,改变是困难的,并期待与社区讨论过渡过程。

跟踪问题已在此处 打开。这是讨论 Tokio 的 async/await 计划和跟踪进度的场所。

Tokio Trace

在处理生产应用程序时,执行行为的可视化至关重要。这包括以下问题,例如

  • 当前正在执行多少任务?
  • 为什么此任务挂起?
  • 哪些任务轮询时间超出预期,原因是什么?

目前,没有好的方法来回答。为了帮助提高 Tokio 的可见性和可调试性,我们(主要是 hawkw)正在开发一项重要的新功能:Tokio Trace。

Tokio trace 已在 问题中讨论过。在高层次上,Tokio trace 是一个结构化日志记录系统,其中日志事件涵盖一段时间,而不是固定点。“一段时间”是一个关键特性。Instrumentation API 将允许指定事件何时开始以及何时结束。通过这样做,我们可以推断父/子关系。包含在其他事件中的事件是子事件,构建出一个树。

一旦构建了父/子依赖关系,就很容易进行诸如过滤与错误任务相关的日志事件之类的操作。还可以跨多个任务跟踪与错误任务相关的日志事件。

Tokio trace 的第二部分是“结构化”部分。事件数据可以使用原始类型包含,而不是记录基本字符串。例如,可以检测 Stream 组合器以跟踪每个 poll 处理的消息数量,方法是执行类似的操作

trace!(messages = num_processed);

其中 num_processed 是一个 usize。订阅者能够接收带有 usize 值的事件。

通过结合父/子结构和结构化日志记录,我们可以通过跟踪任务轮询持续时间的 99.9 百分位数,并检查延迟增加的常见原因(例如,处理包含许多消息但没有 yield 的流),来回答“哪些任务轮询时间超出预期,原因是什么?”的问题。许多这些检查可以通过监听 Tokio trace 发出的事件的工具来实现。

一旦该功能在 Tokio 中正式发布,将会有更多关于此功能的博客文章。

团队

2019 年 Tokio 的最后一个重大事项是我一直在考虑的事情。Tokio 在功能和采用方面都已发展到超出我管理的范围,即使有构成非正式 Tokio 团队的一群杰出的常规贡献者也是如此。现在是时候扩展 Tokio 的开发和维护了。

实现此目标的策略并不新鲜,我认为 Rust 提供了一个很好的模型来遵循。我们将需要引入一组团队,每个团队专注于 Tokio 的各个方面。

关于将存在哪些团队以及谁将成为团队成员的具体细节仍在确定中,并且会随着时间的推移而变化。全年将发布博客文章,讨论这方面的发展。

我们还需要新人来帮助加入构建和发展 Tokio 的工作。这包括我们花费时间进行指导。因此,请将此视为行动前的号召。您在工作中使用 Tokio 吗?或者您只是对 Rust 异步 I/O 感兴趣?无论您是否觉得自己没有所需的经验或太“新手”都没关系。立即加入我们的 Gitter 频道,帮助我们弄清楚过渡到团队的过程。

最后,特别感谢那些超越自我,花费大量时间帮助 Tokio 开发、维护、文档以及在 Gitter 中帮助用户的各位。