Rust in Everything
基于 tokio-metrics 0.5.2

tokio-metrics 源码解析

全卷只对着一个版本写:

crate版本tagcommit日期
tokio-metrics0.5.2v0.5.22d192182026-08-28

tokio 卷讲的是被量的东西:任务怎么被 poll、被唤醒,worker 怎么取任务、怎么偷、怎么睡。这一卷只讲「怎么量」。tokio-metrics 的 src/ 有 6.9k 行,大半是文档注释,去掉注释和空行约 2.4k 行,其中只有一处有算法:怎么在不改调度器的前提下,知道一个任务每次等了多久、跑了多久。全卷沿两条线展开:

  • 任务主线(包一层 future):monitor.instrument(fut) 把 future 包进 Instrumented。每次 poll 先记时间,把调用方的 waker 存起来、换成自己的 waker 再去 poll 内层;内层将来调 wake 时,先记下唤醒时刻再转给真正的 waker。三个时刻(上次 poll 结束、被唤醒、这次 poll 开始)切出「空闲」和「排队」两段,poll 本身的耗时再按阈值分快慢。所有数字都累加进一组 AtomicU64。
  • 运行时主线(读计数器做差):RuntimeMonitor::new(&handle) 只存一个 tokio 的 RuntimeMetrics 句柄;intervals() 每调一次 next(),逐个 worker 读 tokio 的累计计数器,与上一次相减,求和、求最大最小。

两条线最后汇到同一个模式:写的一方只累加,读的一方做差分。TaskIntervals、RuntimeIntervals 都是普通的 Iterator,多久采一次样由调用方自己 sleep 决定。

flowchart TB
  intro[00 导读与全景] --> task[01 任务指标]
  task --> rt[02 运行时指标]
  rt --> export[03 采样与导出]
  export --> design[04 设计取舍]
  design --> pit[05 常见坑]
  pit --> site[06 在本站中]
  site --> hands[07 动手]

各部分

  1. 00 导读与全景:锁定的版本、两条主线、阅读路线、源码地图(三个 feature 和 tokio_unstable);它解决什么问题,和 tokio 自带的指标、tokio-console、dial9 各是什么关系。
  2. 01 任务指标:本卷重点。Instrumented 与 instrument_poll,包装 waker 怎么切出空闲和排队,TaskMetrics 的 19 个字段和 10 个派生指标,TaskMonitorCore 与 FutureMonitor。
  3. 02 运行时指标:稳定与不稳定两层字段,每个字段对应 tokio 调度器的哪段代码;Worker 的差分,poll 时间直方图。
  4. 03 采样与导出:intervals() 的差分细节,同一个 monitor 开两个 intervals() 会互相抢走最大值;metrics-rs 与 metrique 集成。
  5. 04 设计取舍:包 waker 而不改调度器的代价,只累加不清零,不稳定字段用 cfg 删掉。
  6. 05 常见坑:用报错原文或现象做标题,每个都先跑出最小复现:不稳定字段的 E0609、静态 monitor 包的流的 E0599、builder 链式调用的 E0507,以及四个看起来不对的数字。
  7. 06 在本站中:本站没有用 tokio-metrics,这一章在本地分支上实测搜索重建、SSR 请求和 papers 流水线,并核实打开 tokio_unstable 对本站构建的代价。
  8. 07 动手:不依赖 tokio-metrics,从零写一个 Instrumented<F>,和 tokio-metrics 在同一组任务上对照。

从「导读」开始。只想会用的读者,读 00、01 的 TaskMetrics 与派生指标、03 和 05;想读懂实现的,按顺序读。01 是全卷唯一有算法的地方,后面几部分大多是在读 tokio 的计数器。

💬 关联讨论

发起讨论
加载中...