Tokio 运行时发布

2018年3月8日

我很高兴地宣布 Tokio 的新版本发布。此版本包含了 Tokio Runtime 的首次迭代。

tl;dr(太长不看)

以下是如何编写一个基于 Tokio 的多线程服务器

extern crate tokio;

use tokio::net::TcpListener;
use tokio::prelude::*;

fn process(s: TcpStream)
  -> impl Future<Item = (), Error = ()> + Send
{ ... }

let addr = "127.0.0.1:8080".parse().unwrap();
let listener = TcpListener::bind(&addr).unwrap();

let server = listener.incoming()
    .map_err(|e| println!("error = {:?}", e))
    .for_each(|socket| {
        tokio::spawn(process(socket))
    });

tokio::run(server);

其中 process 代表一个用户定义的函数,它接受一个 socket 并返回一个处理它的 future。对于回声服务器,这可能是从 socket 读取所有数据并将其写回同一个 socket。

指南示例已更新为使用 runtime。

什么是 Tokio Runtime?

Rust 异步栈正在演变为一组松耦合的组件。要运行一个基本的网络应用程序,您至少需要一个异步任务执行器和一个 Tokio reactor 实例。由于一切都是解耦的,因此这些各种组件有多种选择,但这会为所有应用程序增加大量的样板代码。

为了帮助缓解这种情况,Tokio 现在提供了 runtime 的概念。这是一个预先配置好的包,包含了运行应用程序所需的所有各种组件。

runtime 的初始版本包括 reactor 以及一个基于 work-stealing(工作窃取) 的线程池,用于调度和执行应用程序的代码。这为应用程序提供了多线程默认设置。

对于大多数应用程序来说,work-stealing 默认设置是理想的。它使用了与 Go、Erlang、.NET、Java (ForkJoin 池) 等类似的策略... Tokio 提供的实现是为在单个线程池上多路复用许多不相关任务的用例而设计的。

使用 Tokio Runtime

如上面的示例所示,使用 Tokio runtime 最简单的方法是使用两个函数

  • tokio::run
  • tokio::spawn.

第一个函数接受一个 future 来启动应用程序并启动 runtime。大致来说,它执行以下操作

  1. 启动 reactor。
  2. 启动线程池。
  3. 将 future 派生到线程池上。
  4. 阻塞线程直到 runtime 变为 idle(空闲)。

一旦所有派生的 future 都已完成,并且绑定到 reactor 的所有 I/O 资源都被释放,runtime 就会变为 idle。

从 runtime 的上下文中。应用程序可以使用 tokio::spawn 将额外的 future 派生到线程池上。

或者,可以直接使用 Runtime 类型。这允许在设置和使用 runtime 方面有更大的灵活性。

未来的改进

这只是 Tokio runtime 的初始版本。即将发布的版本将包含对基于 Tokio 的应用程序有用的额外功能。一篇博客文章即将发布,其中将更详细地介绍路线图。

正如之前提到的,目标是尽早且频繁地发布。提供新功能以使社区能够进行实验。在接下来的几个月中,整个 Tokio 栈将有一个破坏性版本发布,因此 API 中的任何更改都需要在那之前被发现。

Tokio-core

tokio-core 也发布了一个新版本。此版本更新了 tokio-core 以在底层使用 tokio。这使得当前依赖于 tokio-core 的所有现有应用程序和库(如 Hyper)都能够使用 Tokio runtime 带来的改进,而无需进行破坏性更改。

鉴于预计在未来几个月内会发生大量的变动,我们希望帮助简化跨版本之间的过渡。