Tokio 0.3 发布及 1.0 版本的规划
2020 年 10 月 15 日
Tokio 团队很高兴地宣布 Tokio 0.3 的发布。此版本作为 Tokio 1.0 的 Beta 版。API 的粗糙之处已得到修复。此版本是在作为 1.0 版本的一部分进行稳定之前验证更改的机会。由于大多数问题都很小,我们预计从 0.2 升级到 0.3 将很容易。
1.0 版本计划
Tokio 团队计划在 2020 年 12 月底发布 Tokio 1.0。这个日期临近了。我们请求 *您*,Tokio 社区,试用 Tokio 0.3 并通过 GitHub Issues 或我们的 Discord 频道向我们提供反馈。一旦我们发布 Tokio 1.0,我们将承诺以下稳定性保证
- 至少 5 年的维护。
- 在假设的 2.0 版本发布前至少 3 年。
新特性?
Tokio 0.3 的主要变化是
- IO traits 的更改。
- 新的运行时构建器。
- I/O 驱动程序已全面改进
- API 具有面向未来的特性。
您可以在 changelog 上找到完整列表。
IO traits 的更改
在 @sfackler 的 RFC 的引导下,我们更改了 AsyncRead
和 AsyncWrite
traits 以支持读取未初始化的内存。此更改 *仅* 影响实现 AsyncRead
或 AsyncWrite
traits 或手动调用 poll_*
方法的用户。如果使用了 AsyncReadExt
或 AsyncWriteExt
traits,则无需更改。
以下是新的 AsyncRead
的简化版本
pub trait AsyncRead {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>
) -> Poll<Result<()>>;
}
pub struct ReadBuf<'a> {
buf: &'a mut [MaybeUninit<u8>],
filled: usize,
initialized: usize,
}
impl<'a> ReadBuf<'a> {
// functions here, see RFC
}
Tokio 0.2 的 poll_read
方法的 &mut [u8]
参数存在一些意想不到的尖锐问题:AsyncRead
trait 的实现者(而不是最终消费者)可以读取存储在 &mut [u8]
slice 中的数据。如果程序员创建了一个引用未初始化内存的字节可变 slice(&mut [u8]
)(例如,vector 中的多余容量),那么读取 &mut [u8]
数据将导致未定义的行为。新设计通过提供一个跟踪未初始化内存的 ReadBuf
结构来缓解此问题,从而消除了初始化内存的需要。
此外,poll_read_buf
和 poll_write_buf
方法已从这两个 traits 中删除。实际上,这些方法很少被实现。向量化操作将直接在支持它们的类型上实现。
特性标志简化
我们减少了使用 Tokio 核心特性所需的特性标志数量。dns
、tcp
、udp
和 uds
特性标志被折叠为一个 net
特性标志。我们还将 rt-core
和 rt-util
合并为一个 rt
特性标志。此 rt
特性标志现在包含执行 futures 所需的一切,除了多线程运行时,它现在位于 rt-multi-thread
特性标志下(在 0.2 中称为 rt-threaded
)。
dns
,tcp
,udp
和uds
->net
rt-util
和rt-core
->rt
rt-threaded
->rt-multi-thread
运行时和构建器重构
Tokio 团队改进了运行时 Builder
,以在特性标志排列中提供更好的统一性,并更耐误用。为了实现这一点,我们在 Builder
类型中添加了两个新的构造函数,以支持构建 Tokio 运行时的两种变体。此外,Tokio 在使用 Runtime::new
时将不再根据特性标志选择运行时变体,而是始终使用多线程运行时,这仅在 rt-multi-thread
特性标志下可用。此外,我们将 core_threads
构建器方法重命名为更准确的 worker_threads
。
我们还重构了运行时模块。Tokio 0.2 公开了两种类型来与 Tokio 运行时交互:Runtime
和 Handle
。Runtime
有一个需要 &mut self
的 block_on
方法,而 Handle
有一个需要 &self
的 block_on
方法。在 Tokio 0.3 中,我们将 Runtime
和 Handle
折叠为一个 Runtime
。新运行时上的 block_on
方法接受 &self
,这意味着它可以毫无问题地放入 Arc 中。
配置具有 6 个 worker threads 的多线程运行时的示例
use tokio::runtime::Builder;
let rt = Builder::new_multi_thread()
.worker_threads(6)
.enable_all()
.build()
.unwrap();
rt.block_on(async {
println!("Hello world!");
});
方法已更改为 &self
我们彻底修改了在 socket 类型中处理 wakers 的方式,以允许通过 &self
而不是 &mut self
进行并发调用。为了实现这一点,我们不得不重写内部处理 wakers 的方式。问题在于基于 poll_*
的方法的工作方式。在 futures 契约下,它声明 poll_*
fn 应该只存储最后一次调用它的 waker。
请注意,在多次调用 poll 时,只有传递给最近调用的 Context 中的 Waker 才应计划接收唤醒。
来自 Future
这意味着这些 poll_*
方法不能并发调用或唤醒。为了使 &self
在我们的 socket 类型的 async fn 中工作,我们重构了我们的 io 驱动程序,以使用内部侵入式链表来存储 wakers。通过这样做,我们的 socket 类型在被调用时可以接受 &self
而不是 &mut self
。
移除非 1.0 版本 crate 和为 1.0 版本面向未来
为了推进 1.0 版本,我们从公共 API 中移除了许多非 1.0 版本的依赖项。这包括 bytes 和 mio。这将使我们能够推进面向未来的更稳定的 1.0 版本,同时我们继续在我们的底层 crates 中进行创新。
Mio 0.7
我们最终也将 mio 升级到 0.7,它最初于 2019 年 12 月以 alpha 版本发布,但没有赶上 Tokio 0.2。从那时起,mio 0.7 有时间成熟,并最终进入 Tokio 0.3 的这个版本。这升级了一些关键依赖项,例如 winapi 从 0.2 到 0.3。此外,mio 0.7 现在附带了 wepoll
的纯 Rust 实现,wepoll
是一个为基于 Windows 的应用程序实现 epoll API 的库。
0.2 和 0.3 版本之间的兼容性
可以通过 tokio-compat-02
crate 逐步升级到 Tokio 0.3。这个 crate 将从 Tokio 0.2 启动一个单线程后台运行时,并允许您在其上下文中包装任何 future。这将允许您在任何运行时(包括 Tokio 0.3)中运行需要 Tokio 0.2 的库。下面是如何使用 hyper 0.13 发送请求的示例,hyper 0.13 需要 Tokio 0.2。
use hyper::{Client, Uri};
use tokio_compat_02::FutureExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
// This will not panic because we are wrapping it in the
// Tokio 0.2 context via the `FutureExt::compat` fn.
client
.get(Uri::from_static("https://tokio.rust-lang.net.cn"))
.compat()
.await?;
Ok(())
}
结论
自 Tokio 0.2 发布以来,我们已经有超过 50 位贡献者帮助我们。我们非常感谢我们的社区帮助我们找到并修复错误。随着我们继续朝着 1.0 版本迈进,反馈将比以往任何时候都更加重要,因此请随时提出 issue 或加入我们的 Discord,以帮助我们实现目标。