成年人还是得做选择。
seastar::metrics
seastar::metrics 是 seastar 提供的一套机制,用来监控系统的动态指标。
label
在
seastar::metrics
里面,每个指标都有自己的标签。举个例子吧,假设 seastore
能够管理多个存储设备,每个设备都有自己的 IO
队列。而作为一个存储系统,我们可能会关心好多指数
-
total_bytes: 写入设备的数据量
-
total_operations: 读写请求的总个数
-
queue_length: 当前的读写队列长度
-
delay: 总的延迟
问题在于,每个设备我们都需要监控这同一组数据。如果按照面向对象的思路,那么就是每个对象都有一组属性,而且每个对象都有自己的名字或者索引。
另外对于 seastar 应用来说,每个 shard
都是一个相对独立处理的单元,独立结算,自负盈亏。如果我们希望监控一个
sharded service,那么这个服务在每个 shard
都有一组自己的数据。Sesastar 甚至为每一个
metric_definition_impl
都强制加上了 shard_label
,所以每个 metric
从一出生,它标签上的"shard"就是当时 reactor 的 shard
id,如果当时 reactor 还没有运行,那么 shard 就是
"0"。但是如果要自己设置
shard_label
的话,也可以用 seastar 提供的
shard_label
。
label shard_label("shard");
其中 label
是一个 functor 类,它可以用来构造
label_instance
实例。后者才是真的 label。
template<typename T>
instance operator()(T value) const {
return label_instance(key, std::forward<T>(value));
}
通过为 metrics 贴上多个标签,我们可以更方便地管理和查询这些指数。而且不用因为把数据聚合起来而丢失重要的信息。seastar 在把 metrics 导出到监控系统的时候,也把 label 一起导出了。它
-
使用 prometheus labels
-
把 label 编码成 collected 的 type instance 字段里面。
将来,如果我们决定使用
seastar::metrics
的话,甚至可能会用不同的 label
在不同的维度来标记同一个监控的指数
-
不同 CPU shard 上的监控数据
-
不同 pg 的监控数据
-
不同存储设备的监控数据
-
不同网卡或者网络设备的监控数据
-
不同网络连接的监控数据,比如说连接到 peer osd 的心跳或者网络延迟。
比如说,当我们注意到出现多个 slow request 的时候,首先会看这些请求在不同维度上的相关性,如果所有的请求都和某个 replica osd 有关,那么我们可能就能猜测 primary 和这个 osd 的连接是不是有问题。
名字
metric 的名字由 group 和 name 构成。一组逻辑上相关的 metric 组成一个 group,比如说 seastore 的所有指数的 group 可能就是 "seastore"。加上一个名字空间方便管理。内存方面的监控则用 "memory" 作为 group 的名字。
数据的类型
seastar::metrics
大体是按照
Prometheus 的几种指标的类型
和
collectd 的数据类型
来实现的。
它定义了下面几类指标:
-
counter: 单调递增的整数。要是发现某个 counter 变小了,唯一的解释就是它溢出了。比如说,从启动到现在 cache miss 的请求数量。
-
gauge: 测量值。和
counter
不同,guage
支持浮点,它的值允许减小。比如说-
系统的音量
-
某个队列的总延迟
-
当前 onode 的缓存大小
-
-
derive: 和
gauge
相比,derive
更像counter
一些。它仅仅支持整型,但是它允许读数减小。它叫 "derive" 的原因并不是这个指标是由其他指标导出 (derive) 的,而是因为,很多时候我们关心的是这个数值的变化量 (derivative),或者说读数对时间的导数。-
当前正在处理的请求个数
-
-
histogram: 一个指标的直方图。比如说,
-
请求的延迟的分布
-
请求大小的分布
-
为了和
collectd 的类型
对应上,seastar::metrics
还定义了一些方便的函数,用来设置基于这些类型。比如,make_total_bytes()
,它的功用就是在为
collectd 导出监控数据的时候,为对应的指标设置
total_bytes
的数据类型。
数据的来源
make_gauge()
这些函数让我们提供一个变量的引用,或者给出一个函数返回要监控的数据。事实上前者也是通过包装一个
lambda 来实现的。
ceph::common::PerfCounters
ceph::common:PerfCounters
使用
PerfCounters
来管理一组 perf counter,
名字
因为
ceph::common::PerfCounters
不是统一管理的,每个
perfcounter
在构造的时候都设定了一个字符串,作为它的名字。在导出
perfcounter 的时候,把所有的 perfcounter 被放在以
PerfCounters::get_name()
为名的大对象里面。每个
perfcounter 分别打印自己的信息。
数据的类型
none |
u64 |
time |
|
longrunavg |
|||
histogram |
|||
time |
数据的来源
每个 PerfCounters
都有一个
std::vector<>
,用于保存对应的 perf
counter,通过预先定义好的索引来更新和访问 vector
里面对应的值。
ceph |
seastar |
PerfCountersCollection |
metric group |
add_u64_counter() |
make_counter() |
make_derive() |
|
make_gauge() |
|
PerfCountersBuilder |
metric_groups |
PerfCounters |
None |
idx |
metric_id? |
总体上 PerfCounters
和
metrics
两个功能相当,而且前者内置了一些功能
-
对 counter 加上优先级。优先级有点像 Python 的 logging level。它决定了不同情况下,输出 perfcounter 的详尽程度。如果是
CRITICAL
的 perfcounter 的话,一般来说都会打印出来,或者发给 prometheus, influxdb 这些 mgr module。 -
支持设置自定义的字符串作为监控指标的单位,在打印 perfcounter 的时候,可以打印自定义的单位。
-
如果一个 perfcounter 有
LONGRUNAVG
属性,那么还会统计平均值。
但是 PerfCounters
缺少 label
的支持,而且其实现是基于
std::atomic<>
的,在读写 perfcounter
的时候对性能也有负面的影响。