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