8 次重大发布
| 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.2 | 2019 年 2 月 12 日 |
#73 在 #measure
49,501 每月下载量
在 5 个包中(通过 metered)使用
39KB
868 行
metered-rs
为 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指标代替下溢/上溢
- 提供方法以递增或递减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宏,允许报告嵌套枚举错误变体,为嵌套错误提供零成本的错误跟踪(由@w4贡献)
- 扩展
- 0.5.0:
- 使内部指标公开(由@nemosupremo贡献)
- 提供
error_count宏来为错误枚举生成定制的ErrorCount指标计数变体(由@w4贡献) - 使用
Drop自动触发不依赖于结果值的指标(影响InFlight、ResponseTime、Throughput)
- 0.4.0:
- 向生成的结构体添加
allow(missing_docs)(这允许在Rust代码中使用带有lint级别warn(missing_docs)或甚至deny(missing_docs)的metered结构体)(由@reyk贡献) - 为生成的注册表实现
Clear(由@eliaslevy贡献) - 为
RefCell<HdrHistogram>实现Histogram和Clear(由@eliaslevy贡献) - 引入一个具有微秒精度的
Instant(由@eliaslevy贡献)- API破坏性更改:
Instant.elapsed_millis重命名为elapsed_time,并引入了一个新的关联常量,ONE_SEC,用于指定一秒的瞬时单位。
- API破坏性更改:
- 通过重新导出使
AtomicTxPerSec和TxPerSec可见(由@eliaslevy贡献) - 将
StdInstant作为TxPerSec中T: Instant的默认类型参数(由@eliaslevy贡献) - 修改HdrHistogram以与serde_prometheus一起工作(由@w4贡献)
- 请与 serde_prometheus 以及任何 HTTP 服务器一起使用。
- 依赖项更新
indexmap: 1.1 -> 1.3hdrhistogram: 6.3 -> 7.1parking_lot: 0.9 -> 0.10
- 向生成的结构体添加
- 0.3.0:
- 修复了在
async测量方法中保留 span 的问题。 - 更新了夜间构建样本以支持新语法和 Tokio 0.2-alpha(使用 std futures,需要 Rust >= 1.39,nightly 或非 nightly)
- 更新了依赖项以使用
syn,proc-macro2和quote1.0
- 修复了在
- 0.2.2:
- 在
#measured方法中的异步支持不再依赖于异步闭包,因此客户端代码不需要async_closure功能门。 - 更新了依赖项版本
- 在
- 0.2.1:
- 在特定情况下,Serde 会为
PhantomData标记中的ResponseTime和Throughput指标序列化 "nulls"。现在它们被明确排除。
- 在特定情况下,Serde 会为
- 0.2.0:
- 支持
.await语法用户(不再需要await!())
- 支持
- 0.1.3:
- 修复了在
#[measure]’ed 方法中的早期返回问题 - 移除了对 crate
AtomicRefCell的使用,它有时会引发恐慌。 - 支持自定义注册表可见性。
- 支持
async+await!()宏用户。
- 修复了在
使用 Metered
Metered 提供了各种有用的指标,开箱即用。
HitCount:一个计数器,跟踪代码被击中的次数。ErrorCount:一个计数器,跟踪返回的错误数量 -- (适用于返回 stdResult的任何表达式)InFlight:一个仪表,跟踪活跃请求的数量ResponseTime:由 HdrHistogram 支持的表达式持续时间的统计数据Throughput:由 HdrHistogram 支持的表达式每秒钟被调用的次数的统计数据
这些指标通常应用于方法,使用提供的程序宏生成样板代码。
为了提高性能,这些库存指标可以自定义以使用非线程安全(!Sync/!Send)数据结构,但默认情况下会尽可能使用使用无锁策略实现的线程安全数据结构。这是一个为了在各种情况下提供默认值而做出的舒适选择。
Metered 设计为零开销抽象 -- 在这个意义上,高级舒适度不应超过手动添加指标。值得注意的是,库存指标在第一次初始化后 不会 分配内存。然而,它们在每个方法调用时都会触发,因此在热点代码路径中使用较轻的指标(例如 HitCount)和在高级入口点中使用较重的指标(例如 Throughput,ResponseTime)可能很有趣。
如果您需要的指标缺失,或者您想自定义一个指标(例如,跟踪特定错误发生的次数,或根据您的返回类型做出反应),您可以通过实现 trait metered::metric::Metric 来简单地实现自己的指标。
计量器不使用静态变量或共享的全局状态。相反,它允许您使用所需的度量指标构建自己的度量注册表,或者可以使用方法属性为您生成度量注册表。计量器将为每个带有 metered 属性的 impl 块生成一个注册表,该注册表名称由 registry 参数提供。默认情况下,计量器将期望注册表以 self.metrics 的形式访问,但可以通过 registry_expr 属性参数来覆盖表达式。有关更多示例,请参阅演示。
计量器将生成从 Debug 和 serde::Serialize 派生的度量注册表,以便轻松提取您的指标。计量器为每个带有 measure 属性的方法生成一个子注册表,因此组织了层次化的指标。这确保了生成的注册表中访问指标的时间始终是恒定的(并且在可能的情况下,缓存友好),没有任何除指标本身之外的额外开销。
计量器可以愉快地测量任何方法,无论它是否是 async,并且指标将按预期工作(例如,ResponseTime 将返回跨 await 的调用完成的完成时间)。
目前,计量器不提供连接到外部度量存储或监控系统的桥梁。此类支持计划在单独的模块中实现(欢迎贡献!)。
所需的 Rust 版本
计量器从 1.31.0 版本的稳定版 Rust 开始工作。
它不使用任何夜间功能。在某些时候可能会添加一个 nightly 功能标志来使用即将推出的 Rust 功能(例如,const fn),以及计量器所依赖的 crate 中的类似功能,但这优先级较低(欢迎贡献)。
使用过程宏的示例(推荐)
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);
}
}
在上面的片段中,我们将测量 HitCount 和 Throughput。
这通过首先用 metered 注解 impl 块并指定计量器应提供给度量注册表的名称(这里为 BizMetrics)来实现。稍后,计量器将假设访问该存储库的表达式为 self.metrics,因此我们需要在 Biz 中有一个 metrics 字段,其类型为 BizMetrics。可以通过指定另一个注册表达式来使用另一个字段名,例如 #[metered(registry = BizMetrics, registry_expr = self.my_custom_metrics)]。
然后,我们必须使用 measure 属性注释我们希望测量的方法,并指定我们希望应用的指标:这里的指标是实现了 Metric 特性的结构体的类型,您可以定义自己的。由于没有魔法,我们必须确保 self.metrics 可以访问,并且这仅在具有 &self 或 &mut self 接收器的方法上才能正常工作。
让我们看看biz的代码:它是一个阻塞方法,使用rand::random返回,在0到200毫秒之间。由于random具有随机分布,我们可以预期平均睡眠时间为约100毫秒。这意味着每秒大约有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配置应用于它。
可以在impl块或方法上多次添加measure关键字,这将添加到应用的度量列表。添加相同的度量多次将导致名称冲突。
设计
Metered的自定义属性解析支持使用保留关键字和任意的Rust语法。代码已被提取到Synattra项目,该项目在Syn解析器之上提供了有用的方法,用于属性解析。
计量器的指标可以包裹任何代码片段,无论它们是异步块还是非异步块,使用卫生宏来模拟类似面向切面编程的方法。这段代码已经被提取到Aspect-rs项目中!
许可
许可协议为以下之一:
- Apache License 2.0 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可证 (LICENSE-MIT 或 https://open-source.org.cn/licenses/MIT)
任选其一。
贡献
除非您明确声明,否则您有意提交并包含在作品中的任何贡献,根据Apache-2.0许可定义,应以上述双重许可形式,不附加任何额外条款或条件。
依赖项
约2.5MB
约53K SLoC