宣布 Tokio Metrics 0.1

2022年2月18日

今天,我们很高兴宣布 tokio-metrics 的首次发布,这是一个用于获取 Tokio 应用程序的运行时和任务级别指标的 crate。Tokio Metrics 通过提供对生产环境中运行时行为的可见性,使 Tokio 用户更容易调试其应用程序的性能问题。

如今,Tokio 已在亚马逊、微软、Discord 等公司的大规模生产部署中成功使用。然而,我们经常收到工程师在调试问题时提出的问题。也许他们的响应时间不如他们预期的那样。从历史上看,没有现有工具可以提供有效解决这些挑战所需的可观察性。正是这个挑战促使我们在去年宣布了 Tokio Console,这是一个用于调试 Tokio 应用程序的工具。该公告的响应非常棒,我们已经看到它在帮助开发人员。但是,Tokio Console 是一个本地调试工具,尚不能(目前还不能)在生产环境中检测应用程序。

Tokio Metrics 填补了这个空白。这个 crate 提供了对 Tokio 调度器行为的洞察力,以帮助开发人员识别性能问题所在。您可以将这些指标报告到您首选的工具(例如,Grafana、Prometheus、CloudWatch 等)以及您的应用程序级指标。一旦您开始收集指标,您将能够回答诸如此类的问题

  • 调度器是否过载?
  • 我的任务是否运行时间过长而没有让步?
  • 我的任务是否大部分时间都在等待外部事件完成?

Rafael Leite,一位在 S3 上工作的 Amazon Web Services 工程师,在过去一个月一直在使用 tokio-metrics。他说

这些指标对于理解运行时的内部行为非常有帮助,尤其是在负载下。

我们感谢他的早期反馈,这帮助我们迭代了我们需要获取哪些具体指标来发现性能特征的根本原因。我希望当我们继续使用这个 crate 时,我们会收到更多来自你们所有人的反馈。

开始使用

要使用 tokio-metrics,首先将 crate 添加到您的 Cargo.toml 文件

[dependencies]
tokio-metrics = "0.1.0"

由于 tokio-metrics 使用了一些不稳定的 Tokio API,您还必须启用 tokio_unstable 标志。您可以通过将以下内容添加到 crate 根目录中的 .cargo/config 文件中来完成此操作

[build]
rustflags = ["--cfg", "tokio_unstable"]
rustdocflags = ["--cfg", "tokio_unstable"]

接下来,为每个关键任务构建一个 TaskMonitor;例如,对于 Web 服务的每个端点及其关键子任务

// monitor for the `/` endpoint
let monitor_root = tokio_metrics::TaskMonitor::new();

// monitors for the POST /users endpoint
let monitor_create_user = CreateUserMonitors {
    // monitor for the entire endpoint
    route: tokio_metrics::TaskMonitor::new(),
    // monitor for database insertion subtask
    insert: tokio_metrics::TaskMonitor::new(),
};

然后,使用这些监视器来检测关键任务

let app = axum::Router::new()
    // `GET /` goes to `root`
    .route(
        "/",
        axum::routing::get({
            // monitor the tasks that respond to `GET /`
            let monitor = monitor_root.clone();
            move || monitor.instrument(async { "Hello, World!" })
        }),
    )
    // `POST /users` goes to `create_user`
    .route(
        "/users",
        axum::routing::post({
            // monitor the tasks that respond to `POST /users`
            let monitors = monitor_create_user.clone();
            let route = monitors.route.clone();
            move |body| route.instrument(create_user(body, monitors))
        }),
    );

最后,访问指标

// print task metrics for each endpoint every 1s
let metrics_frequency = std::time::Duration::from_secs(1);
tokio::spawn(async move {
   // call `.intervals()` on each monitor to get an endless
   // iterator of metrics sampled from that monitor
   let root_intervals = monitor_root.intervals();
   let create_user_route_intervals =
      monitor_create_user.route.intervals();
   let create_user_insert_intervals =
      monitor_create_user.insert.intervals();

   // zip the metrics streams together
   let create_user_intervals =
      create_user_route_intervals
         .zip(create_user_insert_intervals);
   let intervals = root_intervals.zip(create_user_intervals);

   // print the metrics for each monitor to stdout
   for (root_rt, (create_user_rt, create_user_insert)) in intervals {
      println!("root_route = {:#?}", root_rt);
      println!("create_user_route = {:#?}", create_user_rt);
      println!("create_user_insert = {:#?}", create_user_insert);
      tokio::time::sleep(metrics_frequency).await;
   }
});

您可以在这里找到更多示例,文档中有更详细的介绍。

本次发布是抢先体验版本。tokio-metrics 的工作才刚刚开始。当您开始更深入地了解 Tokio 运行时时,我们预计您会有更多问题,并获得更多关于如何调试应用程序的想法。我们希望听到这些,因为这将为我们继续开发 Tokio Metrics 和 Tokio Console 提供信息。所以,试一试,让我们知道效果如何。请提交 issue在 Discord 上 ping 我们

— Carl Lerche (@carllerche) & Jack Wrenn (@jswrenn)