成年人还是得做选择。

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 一起导出了。它

将来,如果我们决定使用 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?

总体上 PerfCountersmetrics 两个功能相当,而且前者内置了一些功能

  • 对 counter 加上优先级。优先级有点像 Python 的 logging level。它决定了不同情况下,输出 perfcounter 的详尽程度。如果是 CRITICAL 的 perfcounter 的话,一般来说都会打印出来,或者发给 prometheus, influxdb 这些 mgr module。

  • 支持设置自定义的字符串作为监控指标的单位,在打印 perfcounter 的时候,可以打印自定义的单位。

  • 如果一个 perfcounter 有 LONGRUNAVG 属性,那么还会统计平均值。

但是 PerfCounters 缺少 label 的支持,而且其实现是基于 std::atomic<> 的,在读写 perfcounter 的时候对性能也有负面的影响。