主要观点总结
本文介绍了Tokio框架的基本概念、架构和工作原理,包括其在Rust语言中的异步编程模型、任务调度和生命周期等。文章还对比了Tokio与Nginx的调度机制,并探讨了Tokio未来的发展方向,如IO Uring支持、扩展协议栈、日志与调试支持和内部调度算法更新等。此外,文章还涉及了Tokio在不同平台和形式的支持情况。
关键观点总结
关键观点1: Tokio框架简介
关键观点2: Tokio架构和工作原理
关键观点3: 与Nginx的调度对比
关键观点4: Tokio未来发展方向
正文
上述 Future trait中,poll 是核心方法,用于推进状态机的进行。我们的代码不会直接调用 poll,而是通过 Rust 的关键字 .await 来执行这个 Future,await 会被 rust 在编译时生成代码来调用 poll,返回 Poll(见下),如果是 Pending 则被 runtime 挂起(比如重新放到任务队列中)。当有 event 产生时,挂起的 future 会被唤醒,Rust 会再次调用 future 的 poll,如果此时返回 Ready 就执行完成。
pub enumPoll<T> {
Ready(T),
Pending,
}
多级 Future 嵌套时,只有遇到类似 .await 才会推动执行,是协同式调度而不是抢占式调度(Tokio 1.x版本引入抢占机制来缓解饥饿问题,但rust原生基础是协同式调度)。因此 rust 无需提前为 Future 分配独立的栈或堆上内存,是一种零成本抽象。
如下图所示,rust std 中的异步只维护 Future 以及内部方法 poll,具体的任务队列和调度方法由第三方的 runtime 来实现。每次代码执行到 .await 时会进行一次poll,poll 若 ready 则直接退出表示执行完成,poll 若遇到阻塞,则挂起等待事件池来唤醒。当有事件(例如I/O等)唤醒之后,会把该挂起 Future 封装为 task,加入到任务队列中等待调度,runtime 会不断地从任务队列中拿出任务来执行。
承接上文,这一部分主要介绍 Tokio 实现的 runtime架构,如下图所示:
Tokio 中的 Runtime 结构体如下:
pub structRuntime {
scheduler: Scheduler,
handle: Handle,
blocking_pool: BlockingPool,
}
blocking线程 和 worker线程:worker线程是我们要重点关注的运行时轻量级线程,负责调度和任务执行;blocking线程是在这个过程中的所有的阻塞任务,其数量等于所有的worker线程数量+其他控制线程数量,原因是worker线程本身就是一个blocking任务,其他控制线程又包括信号与通道等。
其中 BlockingPool 是专门用来运行阻塞任务的线程池,上述解释已简单概括;Handle 维护了过程中各种handler,本文不重点关注这两项。Scheduler 是“任务池”和“调度器”的封装,也是 Runtime 最核心的部分。
想要使用 Runtime 必须要经过初始化:
tokio::runtime::Builder::new_multi_thread()
.enable_all().worker_threads(threads).thread_name(name)
.build().unwrap(),
build() 即构造了 Runtime 结构,其中最重要的是 Driver 和 Worker 。