随着分布式系统的发展,可观测性在系统中扮演着重要的角色,通过可观测性可以展示系统的内部状态。作为可观测性三个方向之一的 metrics,实现了对系统信息的统计聚合。
Pipy 在 2 月发布的版本 0.20.0-3 加入用于监控的指标和统计 API,内置 Prometheus Exposition 格式 支持。以此提供了 metrics 能力,并可以通过编程的方式进行观测内容的扩展。
Metrics 的访问接口是通过 Pipy 的管理端口暴露出来的,即默认的 :6060/metrics
,可以通过 --admin-port
参数在启动时指定其他端口。
pipy
2022-02-17 14:56:59.750 [INF] [admin] Starting admin service...
2022-02-17 14:56:59.751 [INF] [listener] Listening on port 6060 at ::
复制代码
Pipy 默认只输出了自身相关的如下指标,内容与其接收到 SIGTSTP
信号时输出的内容一致。
curl localhost:6060/metrics
pipy_out_cnt 0
pipy_inb_cnt 2
pipy_inb_cnt{listen="Admin Service"} 2
pipy_inb_cnt{listen="Admin Service",peer="::1"} 2
pipy_ppl_cnt 2
pipy_ppl_cnt{module="",name="Admin Service"} 2
pipy_chk_size 20480
pipy_chk_size{type="inflate"} 0
pipy_chk_size{type="TLS"} 0
pipy_chk_size{type="Unknown"} 0
pipy_chk_size{type="HTTP2 Codec"} 0
pipy_chk_size{type="HTTP Encoder"} 0
pipy_chk_size{type="Inbound"} 20480
pipy_chk_size{type="Command Line Options"} 0
pipy_chk_size{type="MQTT Encoder"} 0
pipy_chk_size{type="Message"} 0
pipy_chk_size{type="connectSOCKS"} 0
pipy_chk_size{type="Script"} 0
pipy_obj_cnt 61
pipy_obj_cnt{type="pipy::stats::Gauge"} 28
pipy_obj_cnt{type="pipy::http::ResponseHead"} 7
pipy_obj_cnt{type="pipy::http::RequestHead"} 2
pipy_obj_cnt{type="pipy::MessageEnd"} 1
pipy_obj_cnt{type="pipy::Message"} 6
pipy_obj_cnt{type="pipy::Inbound"} 3
pipy_obj_cnt{type="pipy::Data"} 5
pipy_obj_cnt{type="Object"} 9
复制代码
有些软件产品动辄输出几百上千条 metrics 不同,而用户真正关心仅仅是其中的部分,并且不同场景下需要关注的也各不相同。Pipy 以可编程的能力为核心,将选择权交给用户,由用户通过简单灵活的编程方式输出所需的内容。
接下来我们就看下如何定制指标的输入,在开始之前我们先了解下指标的种类。
Metrics 类型
既然是支持 Prometheus Exposition 格式,同样也支持 Prometheus 的 metrics 类型。目前 Pipy metrics API 支持 Counter
、Gauge
和 Histogram
,未来也会支持 Summary
。
Counter
:单调增加的计数器,只有在重启的时候才能重置为 0,例如请求数。
Gauge
:瞬态,表示一个可上可下的数值,例如上面的 pipy_inb_cnt
表示当前的入站连接数,会跟根据连接的数量上下变化。
Histogram
:统计学中的直方图,表示的是数据分布的情况,例如请求延迟的分布:5ms、10ms、50ms、100ms,延迟落在各个区间的请求的数量。
Metrics 定义
Metrics 的定义包含两部分:名字和标签(Label)。
Metrics 标签用来区分测量数据的特征。比如请求数,我们可以区分不同 method
和 status
的请求数:
http_requests_count{method="GET",status="200"}
复制代码
注意: 名字以 pipy_
为前缀的指标名为 Pipy 内部使用,在定义 metics 的时候要注意一下。
使用
可以参考 Pipy 内置的 API 的参考文档,运行 pipy
命令后,浏览器中打开stats API 查看。
我们在 tutorial/03-proxy 的基础上进行扩展,为代理增加 metrics 的统计,比如请求数、请求的延迟分布。
记录请求数
这个很容易想到使用 Counter
来完成,每个请求都进行加 1 的操作。同时我们实现前面提到的标签,区分请求的 method
以及响应码。
要记录请求数,要定义一个 Counter
的全局变量。同时我们统计时需要请求头中的 method
和响应头中的 status
,需要在请求进来时保存请求头的内容。
pipy({
_requestCounter: new stats.Counter('http_requests', ['method', 'status']),
_reqHead: null,
})
.listen(8000)
.demuxHTTP('forward')
.pipeline('forward')
.handleMessageStart(
msg => _reqHead = msg.head
)
.muxHTTP('connection', '')
.handleMessageStart(
msg => (
_requestCounter.withLabels(_reqHead.method, msg.head.status).increase()
)
)
.pipeline('connection')
.connect('localhost:8080')
复制代码
stats.Counter
在构造的时候接受两个参数:指标名字和标签的数组。
在记录数据的时候,首先使用 withLabels()
按照构造时定义的顺序传入标签值;然后调用 increase()
方法来将数值加 1,这个方法也接受参数作为增加的值,默认是 1。
Counter
的更多方法,可参考 API 文档。
统计请求延迟分布
统计请求的延迟分布,就要用 Histogram
直方图了。首先来看下其构造方法,其可以接受 3 个参数。其中第一和第三个参数与 Counter
相同,分别是指标名和标签。
它的第二个参数接受一个数组,作为数据分布的桶(bucket)。
在创建 Histogram
类型的全局变量时,我们使用 Math.pow(2, index)
快速填充一个数组来定义桶,同时还要加入全局变量 _reqTime
来记录请求时间。
pipy({
_requestCounter: new stats.Counter('http_requests_count', ['method', 'status']),
_requestLatency: new stats.Histogram('http_request_latency', new Array(16).fill(0).map((_, i) => Math.pow(2, i))),
_reqHead: null,
_reqTime: 0,
})
.listen(8000)
.demuxHTTP('forward')
.pipeline('forward')
.handleMessageStart(
msg => (
_reqHead = msg.head,
_reqTime = Date.now()
)
)
.muxHTTP('connection', '')
.handleMessageStart(
msg => (
_requestCounter.withLabels(_reqHead.method, msg.head.status).increase(),
_requestLatency.observe(Date.now() - _reqTime)
)
)
.pipeline('connection')
.connect('localhost:8080')
复制代码
从代码上可以看到,我们调用了 Histogram
的 observe()
方法,传入请求的延迟时间。
这次并没有提供了 Gauge
的示例,在这个场景下没有适合 Gauge
的指标,其使用比较简单,可以参考 API 文档。
测试
现在我们发送请求测试一下:
curl localhost:8000/hi
Hi, there!
curl localhost:6060/metrics
http_request_latency{le="1"} 1
http_request_latency{le="2"} 1
http_request_latency{le="4"} 1
http_request_latency{le="8"} 1
http_request_latency{le="16"} 1
http_request_latency{le="32"} 1
http_request_latency{le="64"} 1
http_request_latency{le="128"} 1
http_request_latency{le="256"} 1
http_request_latency{le="512"} 1
http_request_latency{le="1024"} 1
http_request_latency{le="2048"} 1
http_request_latency{le="4096"} 1
http_request_latency{le="8192"} 1
http_request_latency{le="16384"} 1
http_request_latency{le="32768"} 1
http_requests_count{method="GET",status="200"} 1
复制代码
/metrics
的结果中,我们只展示了示例定义的指标。
总结
stats
API 进一步丰富了 Pipy 的可观测能力,并以可编程的方式提供了指标定义的灵活性,避免输出无用指标。支持 Prometheus Exposition 格式,方便使用 Prometheus 指标的采集。通过 metrics 的支持,Pipy 也可以为某些不支持 Prometheus 的系统提供兼容性支持,或者在服务治理之Idling场景下,可以通过监控 Pipy 的指标控制业务服务的启动和退出。
Pipy 当前实现了四种类型中的三种,未来会进一步提供 Summary
格式的实现。
还有就是当前 /metrics
端点,需要 Pipy 开启管理端口,而在某些场景下是无法开启管理端口的,后面也会进行优化。
评论