Metric#
Added in version 1.11.0.
ngx_http_metric_module 模块允许创建任意的实时计算指标。这些指标值存储在共享内存中,并在 /status/http/metric_zones/ API 分支中实时显示。
支持多种数据聚合类型(计数器、直方图、移动平均等),并可按任意键进行分组。
当 从源代码构建 时,该模块默认会被构建;可以通过
构建选项 --without-http_metric_module 禁用它。
在来自 我们的软件仓库 的包和镜像中,该模块已包含在 构建中。
备注
重新加载配置时,由 metric_zone 或 metric_complex_zone
创建的区域仅在其存储布局不变时保留已累积的值。
如果区域大小、指标数量或模式与先前的配置不同,如果
average mean 的 count 发生变化,如果
average mean 的 window 被切换为 off 或从
off 切换回来,或者如果 histogram 的桶数量发生变化,
区域将从空开始;届时原因会以 info 级别记录到
错误日志 中。
其他参数,例如 average exp 的 factor 参数、
average mean 的 window 间隔或 histogram
的桶边界,可以在不丢失已累积值的情况下更改。
配置示例#
统计 API 请求:
http {
metric_zone api_requests:1m count;
server {
listen 80;
location /api/ {
allow 127.0.0.1;
deny all;
api /status/;
metric api_requests $http_user_agent on=request;
}
}
}
如果使用此配置向 /api/ 发起请求:
$ curl 127.0.0.1/api/ --user-agent "Firefox"
api_requests 指标会实时更新:
{
"http": {
"metric_zones": {
"api_requests": {
"discarded": 0,
"metrics": {
"Firefox": 1
}
}
}
}
}
指令#
metric#
计算指定共享内存区域 name 的指标值。
参数:
key — 任意字符串(通常是变量),用于对值进行分组。 最大长度为 255 字节。如果键更长,将被截断为 255 字节并附加省略号
...;value — 由所选模式处理的数字(可以是变量)。 如果省略,默认为
0。如果参数无法转换为数字,则默认为1;on— 可选参数,指定何时计算指标:如果为
on=request,在接收到请求时计算;如果为
on=response,在准备响应期间计算;如果为
on=end(默认),在发送响应后计算。
备注
在内部重定向的情况下,on=request 阶段的指标在原始 location 中计算。但是,on=response 和 on=end 指标将在新的 location 中计算。
可以在同一配置级别指定多个 metric 指令。
仅当当前级别没有定义 metric 指令时,
这些指令才会从上一个配置级别继承。
因此,location 块中的单个 metric 会取消从 server 或 http
继承的所有规则,涉及全部三个 on 阶段,而不仅仅是它自己的阶段。
使用示例:
metric requests $http_user_agent=$request_time;
备注
具有空键或无效 key=value 对的指标将被忽略。
省略的 value 被视为 0:
metric foo $bar; # 等同于 $bar=0
这对于 count 模式很有用,该模式忽略数值,只对指标被更新这一事实做出反应。
备注
请记住,变量在不同阶段进行求值。例如,无法在 on=request (接收请求时)使用 $bytes_sent (发送给客户端的字节数)。
metric_complex_zone#
定义一个 复合指标 — 一组具有独立模式的指标。 块体中的每一行定义一个 子指标名称、一个 模式 以及可选的模式 参数。 区域大小必须至少为八个系统内存页:页大小为 4K 时为 32K。
使用示例:
metric_complex_zone requests:1m expire=on discard_key="old" {
# 子指标名称 模式 参数
min_time min;
avg_time average exp factor=60;
max_time max;
total count;
}
在 API 树中,这样的复合指标模板如下所示:
{
"discarded": 3,
"metrics": {
"key1": {
"min_time": 20,
"avg_time": 50,
"max_time": 80,
"total": 2
},
"old": {
"min_time": 3,
"avg_time": 40,
"max_time": 152,
"total": 80
}
}
}
| 数字;共享内存区域中被丢弃的指标数量 |
| 对象;其成员是具有设定键的复合指标。它们是包含一组具有计算值的子指标的对象 |
metric_zone#
创建一个指定 size 大小和给定 name 名称的共享内存区域来存储指标。该区域名称作为 /status/http/metric_zones/ 分支中的节点。
区域大小必须至少为八个系统内存页:页大小为 4K 时为 32K。
参数:
expire=<on|off>— 区域满时的行为:如果为
on,则丢弃最旧的指标(按更新时间)以释放内存供新指标使用;如果为
off(默认)— 丢弃新传入的指标,保留现有条目。
discard_key=<name>— 定义一个键为 name 的指标,用于累积被丢弃指标的值。默认情况下不创建此类指标。保留键不能手动更新。mode — 数据处理算法(参见 操作模式 部分);
parameters — 所选模式的附加设置(例如,
average exp的factor)。
使用示例:
metric_zone request_time:1m max;
在 API 树中,共享内存区域模板如下所示:
{
"discarded": 0,
"metrics": {
"key1": 123,
"key2": 10.5,
}
}
| 数字;共享内存区域中被丢弃的指标数量 |
| 对象;其成员是具有定义键和计算值的指标 |
备注
在 1 MB 区域中,键大小为 39 字节且单一指标模式时,大约可以存储 8,000 个唯一键条目。
操作模式#
可用指标操作模式列表:
count— 计数器;gauge— 仪表(增加/减少);last— 最后接收到的值;min— 最小值;max— 最大值;average exp— 指数移动平均(EMA)(参数factor);average mean— 窗口内的平均值(参数window和count);histogram— 跨"桶"的分布(阈值列表)。
count#
计数器在每次指标更新时将其值增加 1。
默认值 — 0。
备注
任何指标更新(使用任何值)都会单调地将计数器增加 1。
示例:
metric_zone count:1m count;
# 作为复合指标的一部分:
#
# metric_complex_zone count:1m {
# some_metric_name count;
# }
server {
listen 80;
location /metric/ {
metric count KEY;
}
location ~ ^/metric/set/(.+)$ {
metric count KEY=$1;
}
location /api/ {
api /status/http/metric_zones/count/metrics/;
}
}
更新指标:
$ curl 127.0.0.1/metric/
$ curl 127.0.0.1/metric/set/1
$ curl 127.0.0.1/metric/set/23
$ curl 127.0.0.1/metric/set/-32
API 中的预期指标值:
{
"KEY": 4
}
gauge#
gauge 根据传入数字的符号增加或减少其值。正值增加计数器,负值减少计数器。值为 0 时不改变计数器。
默认值 — 0。
示例:
metric_zone gauge:1m gauge;
# 作为复杂指标的一部分:
#
# metric_complex_zone gauge:1m {
# some_metric_name gauge;
# }
server {
listen 80;
location /metric/ {
metric gauge KEY;
}
location ~ ^/metric/set/(.+)$ {
metric gauge KEY=$1;
}
location /api/ {
api /status/http/metric_zones/gauge/metrics/;
}
}
更新指标:
$ curl 127.0.0.1/metric/
API 中的预期指标值:
{
"KEY": 0
}
进一步更新:
$ curl 127.0.0.1/metric/set/5
$ curl 127.0.0.1/metric/set/-5
$ curl 127.0.0.1/metric/set/8
API 中的预期指标值:
{
"KEY": 8
}
last#
存储最后接收到的值,不进行任何聚合。如果省略 value,则使用 0。
示例:
metric_zone last:1m last;
# 作为复杂指标的一部分:
#
# metric_complex_zone last:1m {
# some_metric_name last;
# }
server {
listen 80;
location /metric/ {
metric last KEY;
}
location ~ ^/metric/set/(.+)$ {
metric last KEY=$1;
}
location /api/ {
api /status/http/metric_zones/last/metrics/;
}
}
更新指标:
$ curl 127.0.0.1/metric/
API 中的预期指标值:
{
"KEY": 0
}
进一步更新:
$ curl 127.0.0.1/metric/set/8000
$ curl 127.0.0.1/metric/set/37
$ curl 127.0.0.1/metric/set/-3.5
API 中的预期指标值:
{
"KEY": -3.5
}
min#
保存两个值中的最小值 — 当前存储的值和新值。
示例:
metric_zone min:1m min;
# 作为复杂指标的一部分:
#
# metric_complex_zone min:1m {
# some_metric_name min;
# }
server {
listen 80;
location /metric/ {
metric min KEY;
}
location ~ ^/metric/set/(.+)$ {
metric min KEY=$1;
}
location /api/ {
api /status/http/metric_zones/min/metrics/;
}
}
更新指标:
$ curl 127.0.0.1/metric/set/42.999
$ curl 127.0.0.1/metric/set/-512
$ curl 127.0.0.1/metric/set/1
$ curl 127.0.0.1/metric/
API 中的预期指标值:
{
"KEY": -512
}
max#
保存两个值中的最大值 — 当前存储的值和新值。
示例:
metric_zone max:1m max;
# 作为复杂指标的一部分:
#
# metric_complex_zone max:1m {
# some_metric_name max;
# }
server {
listen 80;
location /metric/ {
metric max KEY;
}
location ~ ^/metric/set/(.+)$ {
metric max KEY=$1;
}
location /api/ {
api /status/http/metric_zones/max/metrics/;
}
}
更新指标:
$ curl 127.0.0.1/metric/set/42.999
$ curl 127.0.0.1/metric/set/-512
$ curl 127.0.0.1/metric/set/1
$ curl 127.0.0.1/metric/
API 中的预期指标值:
{
"KEY": 42.999
}
average exp#
使用 指数平滑 算法计算平均值。
接受可选参数 factor=<number> — 决定新值对平均值影响程度的系数。允许的整数值范围为 0 到 99。默认值为 90。
系数越高,新值的权重越大。如果指定 90,结果将是新值的 90% 加上先前平均值的 10%。
示例:
metric_zone avg_exp:1m average exp factor=60;
# 作为复杂指标的一部分:
#
# metric_complex_zone avg_exp:1m {
# some_metric_name average exp factor=60;
# }
server {
listen 80;
location ~ ^/metric/set/(.+)$ {
metric avg_exp KEY=$1;
}
location /api/ {
api /status/http/metric_zones/avg_exp/metrics/;
}
}
更新指标:
$ curl 127.0.0.1/metric/set/100
$ curl 127.0.0.1/metric/set/200
$ curl 127.0.0.1/metric/set/0
$ curl 127.0.0.1/metric/set/8
$ curl 127.0.0.1/metric/set/30
API 中的预期指标值:
{
"KEY": 30.16
}
average mean#
计算算术平均值。接受可选参数 window=<off|time> 和 count=<number>,分别定义用于平均的时间间隔和样本大小。默认值:window=off(使用整个样本)和 count=10。
备注
例如, window=5s 将仅考虑最近 5 秒内的事件。window 参数不能为 0。count=number 参数控制样本大小(缓存值),以实现更平滑的平均值计算。
示例:
metric_zone avg_mean:1m average mean window=5s count=8;
# 作为复杂指标的一部分:
#
# metric_complex_zone avg_mean:1m {
# some_metric_name average mean window=5s count=8;
# }
server {
listen 80;
location ~ ^/metric/set/(.+)$ {
metric avg_mean KEY=$1;
}
location /api/ {
api /status/http/metric_zones/avg_mean/metrics/;
}
}
更新指标:
$ curl 127.0.0.1/metric/set/0.1
$ curl 127.0.0.1/metric/set/0.1
$ curl 127.0.0.1/metric/set/0.4
$ curl 127.0.0.1/metric/set/10
$ curl 127.0.0.1/metric/set/1
$ curl 127.0.0.1/metric/set/1
API 中的预期指标值:
{
"KEY": 2.1
}
如果从最后一次更新等待 5 秒,预期值将为:
{
"KEY": 0
}
histogram#
创建一组"桶",如果新值不超过桶的阈值,则递增相关计数器。参数以数值阈值列表的形式提供。对于分析分布(如响应时间)很有用。
必需参数是 numbers — 桶的阈值,按升序列出。
备注
桶值 inf 或 +Inf 可用于捕获所有超过最高指定桶的值。
示例:
metric_zone hist:1m histogram 0.1 0.2 0.5 1 2 inf;
# 作为复杂指标的一部分:
#
# metric_complex_zone hist:1m {
# some_metric_name histogram 0.1 0.2 0.5 1 2 inf;
# }
server {
listen 80;
location ~ ^/metric/set/(.+)$ {
metric histogram KEY=$1;
}
location /api/ {
api /status/http/metric_zones/hist/metrics/;
}
}
更新指标:
$ curl 127.0.0.1/metric/set/0.25
API 中的预期指标值:
{
"KEY": {
"0.1": 0,
"0.2": 0,
"0.5": 1,
"1": 1,
"2": 1,
"inf": 1
}
}
进一步更新:
$ curl 127.0.0.1/metric/set/2
API 中的预期指标值:
{
"KEY": {
"0.1": 0,
"0.2": 0,
"0.5": 1,
"1": 1,
"2": 2,
"inf": 2
}
}
进一步更新:
$ curl 127.0.0.1/metric/set/1000
API 中的预期指标值:
{
"KEY": {
"0.1": 0,
"0.2": 0,
"0.5": 1,
"1": 1,
"2": 2,
"inf": 3
}
}
内置变量#
为每个指标创建以下变量:
$metric_<name>$metric_<name>_key$metric_<name>_value
对于复杂指标,会添加额外的变量:
$metric_<name>_value_<metric>
$metric_<name>#
与 metric 指令类似,$metric_<name> 变量设置器可用于更新指标。计算发生在
Rewrite 阶段,
允许从 njs 模块处理指标。
用于设置变量的值必须遵循 key=value 结构。
键和值都可以由文本、变量及其组合构成。
键是用于分组值的任意字符串。值是由所选模式处理的数字。
如果省略,则默认为 0。
如果参数无法转换为数字,则默认为 1。
使用示例:
http {
metric_zone counter:1m count;
# 此时添加了 $metric_counter 变量
server {
listen 80;
location /metric/ {
set $metric_counter $http_user_agent; # 等同于 $http_user_agent=0
}
location /api/ {
allow 127.0.0.1;
deny all;
api /status/http/metric_zones/counter/;
}
}
}
使用 njs 模块计算指标:
http {
js_import metrics.js;
resolver 127.0.0.53;
metric_complex_zone requests:1m {
min_time min;
max_time max;
total count;
}
location /metric/ {
js_content metrics.js_request;
js_fetch_trusted_certificate /path/to/ISRG_Root_X1.pem;
}
location /api/ {
allow 127.0.0.1;
deny all;
api /static/http/metric_zones/requests/;
}
}
文件 metrics.js:
async function js_request(r) {
let start_time = Date.now();
let results = await Promise.all([ngx.fetch('https://google.com/'),
ngx.fetch('https://google.ru/')]);
// 使用 $metric_requests 变量设置器
r.variables.metric_requests = `google={Date.now() - start_time}`;
}
export default {js_request};
在对 location /metric/ 发起多次请求后,值可能如下所示:
{
"discarded": 0,
"metrics": {
"google": {
"min_time": 70,
"max_time": 432,
"total": 6
}
}
}
备注
设置变量后,可以获取其值;它将等于
指定的 key=value 对。
此外,存储在 $metric_<name>_key 变量中的值
将更改为指定的键。
$metric_<name>_key 和 $metric_<name>_value#
$metric_<name>_key 和 $metric_<name>_value 变量
分别定义键和值。当设置 $metric_<name>_value 时会发生指标更新,
前提是 $metric_<name>_key 中的键已经定义。
备注
对于复杂指标,$metric_<name>_value 变量中的子指标值
使用 ", " 分隔符连接。
使用示例:
http {
metric_zone gauge:1m gauge;
# 此处添加了变量 $metric_gauge、$metric_gauge_key 和 $metric_gauge_value。
metric_complex_zone complex:1m {
hist histogram 1 2 3;
avg average exp;
}
# 此处添加了 $metric_complex、$metric_complex_key 和 $metric_complex_value。
server {
listen 80;
location /gauge/ {
set $metric_gauge_key "foo";
set $metric_gauge_value 1;
# 或者:set $metric_gauge foo=1;
return 200 "Updated with '$metric_gauge'\nValue='$metric_gauge_value'\n";
}
location /complex/ {
set $metric_complex_key "foo";
set $metric_complex_value 3;
# 或者:set $metric_complex foo=3;
return 200 "Updated with '$metric_complex'\nValue='$metric_complex_value'\n";
}
}
}
使用此配置,对 /gauge/ 的请求产生:
$ curl 127.0.0.1/gauge/
Updated with 'foo=1'
Value='1'
对于 /complex/:
$ curl 127.0.0.1/complex/
Updated with 'foo=3'
Value='0 0 1, 3'
备注
如果将空字符串赋值给 $metric_<name>_value,该值将被
识别为 0。如果字符串由无法转换为数字的字符组成,
则被识别为 1。
仅在 $metric_<name>_key 和
$metric_<name>_value 都已设置后才会进行计算。
在这种情况下,存储在 $metric_<name> 中的值将等于
新的 key=value 对。
$metric_<name>_key 中的值表示通过变量指定的最后一个键。
$metric_<name>_value 中的值表示为
$metric_<name>_key 中设置的键计算的最后一个值。
$metric_<name>_value_<metric>#
对于复杂指标,可以使用 $metric_<name>_value_<metric> 变量获取
特定子指标的值,其中 <metric> 是子指标的名称。
使用示例:
http {
metric_complex_zone foo:1m {
count count;
min min;
avg average exp;
}
# 添加 $metric_foo、$metric_foo_key、$metric_foo_value,
# 以及 $metric_foo_value_count、$metric_foo_value_min、$metric_foo_value_avg。
server {
listen 80;
location /foo/ {
set $metric_foo_key bar;
set $metric_foo_value 9;
# 或者:set $metric_foo bar=9;
return 200 "Updated with '$metric_foo'\nValues='$metric_foo_value'\nCount='$metric_foo_value_count'\n";
}
}
}
使用此配置,对 /foo/ 的请求产生:
$ curl 127.0.0.1/foo/
Updated with 'bar=9'
Values='1, 9, 9'
Count='1'
其他示例#
监控 HTTP 方法#
metric_zone http_methods:1m count;
server {
listen 80;
location / {
metric http_methods $request_method;
}
location /metrics/ {
allow 127.0.0.1;
deny all;
api /status/http/metric_zones/http_methods/metrics/;
}
}
响应:
{
"GET": 65,
"POST": 20,
"PUT": 10,
"DELETE": 5
}
上游响应时间分布#
metric_zone upstream_time:10m expire=on histogram
0.05 0.1 0.3 0.5 1 2 5 10 inf;
server {
listen 80;
location /backend/ {
proxy_pass http://backend;
metric upstream_time $upstream_addr=$upstream_response_time on=end;
}
location /metrics/ {
allow 127.0.0.1;
deny all;
api /status/http/metric_zones/upstream_time/;
}
}
响应:
{
"discarded": 0,
"metrics": {
"backend1:8080": {
"0.05": 12,
"0.1": 28,
"0.3": 56,
"0.5": 78,
"1": 92,
"2": 97,
"5": 99,
"10": 100,
"inf": 100
}
}
}
活动连接数#
metric_zone active_connections:2m gauge;
server {
listen 80;
server_name site1.com;
location / {
# 连接时增加
metric active_connections site1=1 on=request;
# 结束时减少
metric active_connections site1=-1 on=end;
}
}
server {
listen 80;
server_name site2.com;
location / {
metric active_connections site2=1 on=request;
metric active_connections site2=-1 on=end;
}
}
server {
listen 8080;
location /connections/ {
allow 127.0.0.1;
deny all;
api /status/http/metric_zones/active_connections/metrics;
}
}
响应:
{
"site1": 42,
"site2": 17
}
Prometheus 支持#
Angie 包含一个内置的 Prometheus 模块,用于以 Prometheus 格式 显示指标,该模块支持自定义指标。
作为集成示例,请考虑以下配置:
http {
# 创建 "upload" 指标
metric_complex_zone upload:1m discard_key="other" {
stats histogram 64 256 1024 4096 16384 +Inf;
sum gauge;
count count;
avg_size average exp;
}
# 描述 "upload" 指标的 Prometheus 模板
prometheus_template upload_metric {
'stats{le="$1"}' $p8s_value
path=~^/http/metric_zones/upload/metrics/angie/stats/(.+)$
type=histogram;
'stats_sum' $p8s_value
path=/http/metric_zones/upload/metrics/angie/sum;
'stats_count' $p8s_value
path=/http/metric_zones/upload/metrics/angie/count;
'avg_size' $p8s_value
path=/http/metric_zones/upload/metrics/angie/avg_size;
}
server {
listen 80;
# 更新指标
location ~ ^/upload/(.*)$ {
api /status/http/metric_zones/upload/metrics/angie/;
metric upload angie=$1 on=request;
}
# 指标抓取目标
location /prometheus/upload_metric/ {
prometheus upload_metric;
}
}
}
在对 /upload/... 发起多次请求后:
$ curl 127.0.0.1/upload/16384
$ curl 127.0.0.1/upload/64448
$ curl 127.0.0.1/upload/64
$ curl 127.0.0.1/upload/1028
$ curl 127.0.0.1/upload/1028
指标值将为:
{
"stats": {
"64": 1,
"256": 1,
"1024": 1,
"4096": 3,
"16384": 4,
"+Inf": 5
},
"sum": 82952,
"count": 5,
"avg_size": 1077.9376
}
以 Prometheus 格式,
该指标可在 /prometheus/upload_metric/ 获取:
# Angie Prometheus template "upload_metric"
# TYPE stats histogram
stats{le="64"} 1
stats{le="256"} 1
stats{le="1024"} 1
stats{le="4096"} 3
stats{le="16384"} 4
stats{le="+Inf"} 5
stats_sum 82952
stats_count 5
avg_size 1077.9376