Async Rust中的future可以任意组合或嵌套,以实现各种控制流。假设每个Future的执行都表示为一个节点,那么可以将异步任务的异步执行组织到一个逻辑树中,该逻辑树在Future的轮询、完成和取消过程中不断转换。
在本文中,我们将介绍Await-Tree,一个Async Rust的调试工具。它可以分析任务中的异步调用链和任务之间的依赖阻塞关系,以最小的运行时开销显著提高系统的可观察性和可调试性。await-tree允许开发人员在运行时转储这个执行树,每个Future的跨度由instrument_await注释。
下面我们看一个基本示例:
在Cargo.toml文件中,加入以下依赖项:
[dependencies]await-tree = "0.1.2"futures = "0.3.30"tokio = {version = "1.35.1", features = ["full"]}
代码如下:
use std::time::Duration;use await_tree::{Config, InstrumentAwait, Registry};use futures::future::{join, pending};use tokio::time::sleep;async fn bar(i: i32) { // `&'static str` span baz(i).instrument_await("baz in bar").await}async fn baz(i: i32) { // runtime `String` span is also supported pending() .instrument_await(format!("pending in baz {i}")) .await}async fn foo() { // spans of joined futures will be siblings in the tree join( bar(3).instrument_await("bar"), baz(2).instrument_await("baz"), ) .await;}#[tokio::main]async fn main() { let mut registry = Registry::new(Config::default()); let root = registry.register((), "foo"); tokio::spawn(root.instrument(foo())); sleep(Duration::from_secs(1)).await; let tree = registry.get(&()).unwrap().to_string(); println!("{tree}");}
执行cargo run,结果如下:
foo [1.002s] baz [1.002s] pending in baz 2 [1.002s] bar [1.002s] baz in bar [1.002s] pending in baz 3 [1.002s]
在代码中,我们有一些简单的async函数嵌套调用和使用join并发执行。与通常的代码不同,我们在希望跟踪的每个关键future后面添加.instrument_await,并为其指定名称。此名称可以是静态字符串常量,也可以包含其他运行时信息。
我们再看另外一个例子:
use std::time::Duration;use await_tree::{Config, InstrumentAwait, Registry};use futures::channel::oneshot::{self, Receiver};use futures::future::{pending, select};use futures::FutureExt;use tokio::time::sleep;async fn work(rx: Receiver<()>) { let mut fut = pending().instrument_await("fut"); let _ = select( sleep(Duration::from_millis(500)) .instrument_await("sleep") .boxed(), &mut fut, ) .instrument_await("select") .await; // 等待信号继续 rx.instrument_await("rx").await.unwrap(); fut.await}#[tokio::main]async fn main() { let mut registry = Registry::new(Config::default()); let root = registry.register((), "work"); let (tx, rx) = oneshot::channel(); tokio::spawn(root.instrument(work(rx))); sleep(Duration::from_millis(100)).await; let tree = registry.get(&()).unwrap().to_string(); println!("{tree}"); sleep(Duration::from_secs(1)).await; let tree = registry.get(&()).unwrap().to_string(); println!("{tree}"); tx.send(()).unwrap(); sleep(Duration::from_secs(1)).await; let tree = registry.get(&()).unwrap().to_string(); println!("{tree}");}
结果如下:
work [101.181ms] select [101.066ms] fut [101.044ms] sleep [101.044ms]work [1.103s] rx [601.779ms][Detached 4] fut [1.103s]work [2.105s] fut [2.105s]
这个例子展示了如何从树中分离并重新挂载一个span。
在本文中,我们介绍了await- tree作为Async Rust中可观察性的强大工具。await- tree是为Async Rust原生设计的回溯工具,它允许开发者实时观察每个异步任务的执行状态,并分析不同future或任务之间的依赖阻塞关系。
本文链接:http://www.28at.com/showinfo-26-74193-0.htmlRust异步编程的可观察调试工具:Await-Tree
声明:本网页内容旨在传播知识,不代表本站观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。