Rust异步编程的可观察调试工具:Await-Tree
创始人
2025-07-12 11:21:43
0

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或任务之间的依赖阻塞关系。

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...