11 个版本 (破坏性更新)

0.9.0 2022 年 6 月 5 日
0.8.0 2021 年 3 月 26 日
0.7.0 2020 年 10 月 31 日
0.4.0 2020 年 5 月 25 日
0.1.3 2019 年 2 月 12 日

#31性能分析

Download history 16998/week @ 2024-03-14 13842/week @ 2024-03-21 12859/week @ 2024-03-28 16509/week @ 2024-04-04 14524/week @ 2024-04-11 15922/week @ 2024-04-18 18498/week @ 2024-04-25 18767/week @ 2024-05-02 17529/week @ 2024-05-09 15991/week @ 2024-05-16 17420/week @ 2024-05-23 14975/week @ 2024-05-30 14633/week @ 2024-06-06 14670/week @ 2024-06-13 11694/week @ 2024-06-20 9436/week @ 2024-06-27

53,524 每月下载量
4 个crate 中使用

Apache-2.0 OR MIT

53KB
987

metered-rs

Build Status License Cargo Documentation Rust 1.31+

快速、易用的 Rust 性能指标!

Metered 帮助您在生产环境中测量程序的性能。受到 Coda Hale 的 Java 指标库的启发,Metered 通过提供声明式和过程宏来测量程序,而无需更改您的逻辑,使得实时测量变得简单。

Metered 考虑到以下原则构建

  • 高可用性但无魔法:测量代码只需在代码上添加注解即可。Metered 允许您从裸指标构建自己的指标注册表,或使用过程宏自动生成。它不使用共享的全局变量或静态变量。

  • 恒定,非常低的开销:良好的可用性不应该带来开销;唯一的开销是实际指标后端(例如计数器、仪表、直方图)带来的开销,而 Metered 中提供的那些在初始化后不会分配。Metered 将生成指标注册表作为常规 Rust struct,因此查找指标时不需要查找。Metered 提供了无同步和线程安全的指标后端,以便单线程或无共享架构不需要为同步付费。在可能的情况下,Metered 提供的线程安全指标后端使用无锁数据结构。

  • 可扩展:指标只是实现了具有特定行为的 Metric 特性的常规类型。Metered 的宏允许您引用任何 Rust 类型,从而实现用户可扩展的属性!

许多指标只有在获得精确统计数据时才有意义。当涉及到低延迟、高范围的直方图时,没有比 Gil Tene 的高动态范围直方图 更好的选择,而 Metered 默认使用 官方 Rust 端口

变更日志

  • 0.9.0:
    • 将整型指标包装而不是下溢/上溢
    • 提供方法以递增或递减 int 指标超过 1,适用于批量计算
    • 添加对 Clear 的泛型实现(由 @plankton6 贡献)
    • HdrHistogram 添加 len 方法(由 @plankton6 贡献)
    • 代码质量修复和依赖项更新
  • 0.8.0:
    • 通过 OnResultMut 而不是 OnResult 来更新指标,以支持需要可变访问结果的指标 - 例如,消费一个 Stream(由 @w4 贡献)
  • 0.7.0:
    • 公开内部指标后端类型 Throughput(修复问题 #30)
    • 为所有顶级指标实现 Deref
    • 公开内部指标后端类型 Throughput
    • error_count 属性添加 skip_cleared 选项(由 @w4 贡献)
      • 引入一个新的 Clearable 特征,该特征公开了实现 Clear 的指标的行 为(为了向后兼容)。目前仅在计数器上实现。
      • 默认行为可以通过构建时功能 error-count-skip-cleared-by-default 控制
  • 0.6.0:
    • 扩展 error_count 宏,允许报告 nested 枚举错误变体,为嵌套错误提供零成本的错误跟踪(由 @w4 贡献)
  • 0.5.0:
    • 将内部指标公开(由 @nemosupremo 贡献)
    • 提供 error_count 宏以生成针对错误枚举的定制的 ErrorCount 指标计数变体(由 @w4 贡献)
    • 使用 Drop 自动触发不依赖于结果值的指标(影响 InFlightResponseTimeThroughput
  • 0.4.0:
    • allow(missing_docs) 添加到生成的结构体中(这允许在 Rust 代码中使用带有 lint 级别 warn(missing_docs) 或甚至 deny(missing_docs) 的 metered 结构体)(由 @reyk 贡献)
    • 为生成的注册表实现 Clear(由 @eliaslevy 贡献)
    • RefCell<HdrHistogram> 实现 HistogramClear(由 @eliaslevy 贡献)
    • 引入具有微秒精度的 Instant(由 @eliaslevy 贡献)
      • API 破坏性更改:将 Instant.elapsed_millis 重命名为 elapsed_time,并引入一个新关联常量 ONE_SEC,用于指定 instant 单位中的一秒。
    • 通过重新导出使 AtomicTxPerSecTxPerSec 可见(由 @eliaslevy 贡献)
    • StdInstant 作为 TxPerSecT: Instant 的默认类型参数(由 @eliaslevy 贡献)
    • 修改 HdrHistogram 以与 serde_prometheus 一起使用(由 @w4 贡献)
    • 升级依赖项
      • indexmap: 1.1 -> 1.3
      • hdrhistogram: 6.3 -> 7.1
      • parking_lot: 0.9 -> 0.10
  • 0.3.0:
    • 修复以在 async 测量方法中保留跨度。
    • 更新 nightly 示例以使用新语法和 Tokio 0.2-alpha(使用 std futures,需要 Rust >= 1.39,夜间或非夜间)
    • 将依赖项更新到使用 synproc-macro2quote 1.0
  • 0.2.2:
    • #measured 方法中,异步支持不再依赖于异步闭包,因此客户端代码不需要使用 async_closure 功能门。
    • 更新依赖版本
  • 0.2.1:
    • 在某些情况下,Serde 会为 PhantomData 标记在 ResponseTimeThroughput 指标中序列化 "nulls"。现在它们被明确排除。
  • 0.2.0:
    • 支持 .await 语法用户(不再需要 await!()
  • 0.1.3:
    • 修复了 #[measure]'ed 方法中的提前返回问题
    • 移除了 AtomicRefCell 的使用,这有时会导致 panic。
    • 支持自定义注册可见性。
    • 支持 async + await!() 宏用户。

使用 Metered

Metered 提供了多种实用的指标,开箱即用。

  • HitCount:一个计数器,跟踪代码被触发的次数。
  • ErrorCount:一个计数器,跟踪返回了多少错误(适用于任何返回 std Result 的表达式)
  • InFlight:一个仪表,跟踪有多少请求是活跃的
  • ResponseTime:基于 HdrHistogram 的表达式的持续时间统计
  • Throughput:基于 HdrHistogram 的每秒调用表达式的次数统计

这些指标通常应用于方法,使用提供的程序宏生成样板代码。

为了获得更高的性能,这些库存指标可以根据需要定制为使用非线程安全(!Sync/!Send)数据结构,但在可能的情况下,默认使用线程安全的数据结构,这些数据结构使用无锁策略实现。这是一个为了在各种情况下都有效而做出的折衷选择。

Metered 被设计为无开销的抽象——也就是说,高级别的人体工程学不应该比手动添加指标更昂贵。值得注意的是,库存指标在首次初始化后不会分配内存。然而,它们在每个方法调用时都会触发,因此使用较轻的指标(例如 HitCount)在热代码路径中可能很有趣,而在高级别入口点中使用较重的指标(例如 ThroughputResponseTime)可能更有利。

如果您需要缺失的指标,或者您想自定义指标(例如,跟踪特定错误发生的次数,或根据您的返回类型做出反应),您可以通过实现 metered::metric::Metric 特性来简单地实现自己的指标。

计量工具不使用静态变量或共享的全局状态。相反,它允许您使用所需的指标自行构建自己的指标注册表,或者可以使用方法属性为您生成指标注册表。计量工具将为每个带有 metered 属性注解的 impl 块生成一个注册表,该注册表名称由 registry 参数提供。默认情况下,计量工具期望注册表以 self.metrics 的方式访问,但可以使用 registry_expr 属性参数来覆盖表达式。有关更多示例,请参阅演示。

计量工具将生成继承自 Debugserde::Serialize 的指标注册表,以便轻松提取您的指标。计量工具为每个带有 measure 属性的方法生成一个子注册表,从而组织层次结构化的指标。这确保了在生成的注册表中访问指标的时间始终是常数(并且,在可能的情况下,缓存友好),除了指标本身之外没有其他开销。

计量工具可以愉快地测量任何方法,无论它是 async 还是非 async,并且指标将按预期工作(例如,ResponseTime 将返回跨 await 调用的完成时间)。

目前,计量工具不提供连接到外部指标存储或监控系统的桥梁。此类支持计划在单独的模块中(欢迎贡献!)。

所需的 Rust 版本

计量工具从 1.31.0 版本的稳定版 Rust 开始工作。

它不使用任何夜间功能。在某些时候可能会出现一个 nightly 功能标志来使用即将推出的 Rust 功能(如 const fn),以及计量工具依赖项中的类似功能,但这优先级较低(欢迎贡献)。

use metered::{metered, Throughput, HitCount};

#[derive(Default, Debug, serde::Serialize)]
pub struct Biz {
    metrics: BizMetrics,
}

#[metered(registry = BizMetrics)]
impl Biz {
    #[measure([HitCount, Throughput])]
    pub fn biz(&self) {        
        let delay = std::time::Duration::from_millis(rand::random::<u64>() % 200);
        std::thread::sleep(delay);
    }   
}

在上面的代码片段中,我们将测量 biz 方法的 HitCountThroughput

这是通过首先注解 impl 块并指定计量工具应给指标注册表提供的名称(在此处为 BizMetrics)来实现的。稍后,计量工具将假设访问该存储库的表达式是 self.metrics,因此我们需要在 Biz 中具有 BizMetrics 类型的 metrics 字段。可以通过指定另一个注册表达式来使用另一个字段名,例如 #[metered(registry = BizMetrics, registry_expr = self.my_custom_metrics)]

然后,我们必须使用 measure 属性来注释我们想要测量的方法,指定我们希望应用的度量标准:这里的度量标准只是实现 Metric 特质的结构类型,您可以定义自己的。因为没有魔法,我们必须确保 self.metrics 可以被访问,并且这只能在具有 &self&mut self 接收器的 method 上工作。

让我们再次看看 biz 的代码:它是一个阻塞方法,在 0 到 200ms 之间返回,使用 rand::random。由于 random 具有随机分布,我们可以期望平均睡眠时间为约 100ms。这意味着每秒大约有 10 次调用。

在接下来的测试中,我们启动 5 个线程,每个线程将调用 biz() 200 次。因此,我们可以期望命中计数为 1000,大约需要 20 秒(这意味着 20 个样本,因为我们每秒收集一个样本),并且大约每秒 50 次调用(每个线程 10 次,有 5 个线程)。

use std::thread;
use std::sync::Arc;

fn test_biz() {
    let biz = Arc::new(Biz::default());
    let mut threads = Vec::new();
    for _ in 0..5 {
        let biz = Arc::clone(&biz);
        let t = thread::spawn(move || {
            for _ in 0..200 {
                biz.biz();
            }
        });
        threads.push(t);
    }
    for t in threads {
        t.join().unwrap();
    }
    // Print the results!
    let serialized = serde_yaml::to_string(&*biz).unwrap();
    println!("{}", serialized);
}

然后我们可以使用 serde 将我们的类型序列化为 YAML

metrics:
  biz:
    hit_count: 1000
    throughput:
      - samples: 20
        min: 35
        max: 58
        mean: 49.75
        stdev: 5.146600819958742
        90%ile: 55
        95%ile: 55
        99%ile: 58
        99.9%ile: 58
        99.99%ile: 58
      - ~

我们看到我们确实有每秒 49.75 次调用的平均值,这与我们的预期相符。

支持这些统计数据的 Hdr Histogram 能够提供比固定百分位数更多的信息,但在使用文本时这是一个实用的视图。为了更好的性能分析,请观看 Gil Tene 的演讲 ;-)。

宏参考

metered 属性

#[metered(注册表=您的注册表名称,注册表表达式=self.wrapper.my_registry)]

registry 是必需的,并且必须是一个有效的 Rust 标识符。

registry_expr 默认为 self.metrics,其他值必须是一个有效的 Rust 表达式。此设置允许您配置解析为注册表的表达式。请注意,这会触发该表达式的不可变借用。

visibility 默认为 pub(crate),并且必须是一个有效的结构 Rust 可见性(例如,pub<nothing>pub(self) 等)。此设置允许您更改生成的注册表 struct 的可见性。注册表字段始终是公共的,并以蛇形方法或度量标准命名。

measure 属性

单个度量标准

#[measure(path::to::MyMetric<u64>)]

#[measure(类型=path::to::MyMetric<u64>)]

多个度量标准

#[measure([path::to::MyMetric<u64>,path::AnotherMetric])]

#[measure(类型=[path::to::MyMetric<u64>,path::AnotherMetric])]

type 关键字是被允许的,因为其他关键字计划用于未来的额外属性(例如,实例化选项)。

当将 measure 属性应用于 impl 块时,它适用于所有具有 measure 属性的方法。如果一个方法不需要额外的度量信息,可以通过简单地使用 #[measure] 来注释它,并且 impl 块的 measure 配置将被应用。

measure 关键字可以在一个 impl 块或方法上多次添加,这将添加到应用度量列表中。添加相同的度量多次会导致名称冲突。

设计

Metered 的自定义属性解析支持使用保留关键字和任意的 Rust 语法。代码已被提取到 Synattra 项目中,该项目在 Syn 解析器之上提供了用于属性解析的有用方法。

Metered 的度量可以包装任何代码片段,无论它们是否是 async 块,都使用卫生宏来模拟类似于面向方面编程的方法。该代码已被提取到 Aspect-rs 项目中!

许可协议

在以下任一许可协议下获得许可

由您选择。

贡献

除非您明确声明,否则根据 Apache-2.0 许可协议定义的您提交的任何有意包含在工作中的贡献,将按照上述方式双许可,不附加任何其他条款或条件。

依赖项

~3–9.5MB
~80K SLoC