Metric#
Модуль ngx_stream_metric_module позволяет создавать вычисляемые в реальном
времени произвольные метрики для потокового (TCP и UDP) трафика. Значения таких
метрик сохраняются в разделяемой памяти и отображаются в реальном времени в ветке
API /status/stream/metric_zones/. Поддерживаются различные типы
агрегации данных (счетчики, гистограммы, скользящие средние и др.) с
группировкой по произвольным ключам.
Схема ответа приведена в справочнике API зон потоковых метрик.
Примечание
При перезагрузке конфигурации зона, созданная директивой
metric_zone или
metric_complex_zone, сохраняет накопленные
значения только при неизменной структуре хранимых данных. Зона
начинает работу пустой, если от предыдущей конфигурации отличаются
размер зоны, количество метрик или режим, если изменился
параметр count режима average mean, если параметр
window режима average mean переключен в off или
обратно или если изменилось количество корзин histogram;
причина при этом
записывается в error_log на уровне info. Остальные
параметры, например factor режима average exp,
интервал window режима average mean или границы корзин
histogram, можно менять без потери накопленных значений.
Пример конфигурации#
Подсчет соединений по адресу клиента:
stream {
metric_zone connections:1m count;
server {
listen 12345;
metric connections $remote_addr on=connect;
}
}
http {
server {
listen 80;
location /status/ {
allow 127.0.0.1;
deny all;
api /status/;
}
}
}
Если выполнить соединение с портом 12345:
$ nc 127.0.0.1 12345 </dev/null
В реальном времени происходит обновление метрики connections:
{
"stream": {
"metric_zones": {
"connections": {
"discarded": 0,
"metrics": {
"127.0.0.1": 1
}
}
}
}
}
Директивы#
metric#
| |
По умолчанию | — |
stream, server |
Подсчитывает значение метрики с указанным названием зоны разделяемой памяти.
Параметры:
ключ — произвольная строка (часто переменная), по которой группируются значения. Максимальная длина — 255 байт; более длинные ключи обрезаются. В выводе API Angie добавляет
...к любому ключу длиной 255 байт, даже если до сохранения его длина составляла ровно 255 байт. Ключ может сам содержать символы=— значением считается только текст после последнего=, поэтому все, что стоит перед ним (включая вложенные=), становится ключом;значение — число (может быть переменной), обрабатываемое выбранным режимом. Если пропущено, считается
0. Если параметр невозможно преобразовать в число, считается1;on— необязательный параметр, указывающий, в какой момент обработки сессии происходит подсчет метрики:Если
on=connect, подсчет происходит на фазеPre-access, до чтения каких-либо данных от клиента;Если
on=preread, подсчет происходит один раз за сессию — в момент, когда клиенту отправляются первые данные, полученные от проксируемого с помощью proxy_pass сервера или сформированные директивой return; к этому моменту такие модули, какproxy_protocol, ssl_preread иmqtt_preread, уже обработали начальные данные клиента;Если
on=end(по умолчанию), подсчет происходит на фазеLog, когда сессия завершается.
Директив metric может быть несколько. Директивы наследуются
с предыдущего уровня конфигурации при условии, что на данном
уровне не описаны свои директивы metric. Поэтому одна
директива metric в блоке server отменяет все правила,
унаследованные от stream, на всех трех стадиях on, а не
только на своей.
Пример использования:
metric sessions $remote_addr=$session_time on=end;
Примечание
Метрики с пустым ключом или неправильной парой ключ=значение
не учитываются. Пропущенное значение учитывается как 0:
metric foo $bar; # Эквивалентно $bar=0
Что полезно, например, для режима count, который игнорирует
числовое значение и реагирует лишь на факт обновления метрики.
Примечание
Важно помнить, что переменные вычисляются в разных фазах. Например,
невозможно использовать $upstream_bytes_sent (количество байт,
отправленных апстриму) при on=connect (до того, как соединение
куда-либо проксировано).
metric_complex_zone#
| |
По умолчанию | — |
stream |
Определяет составную метрику — набор метрик с независимыми режимами. Каждая строка в теле блока задает название подметрики, режим и необязательные параметры режима. Размер зоны должен составлять не менее восьми системных страниц памяти.
Пример использования:
metric_complex_zone sessions:1m expire=on discard_key="old" {
# название подметрики режим работы параметры
min_time min;
avg_time average exp factor=60;
max_time max;
total count;
}
В API дереве шаблон такой составной метрики выглядит следующим образом:
{
"discarded": 3,
"metrics": {
"ключ1": {
"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#
| |
По умолчанию | — |
stream |
Создает зону разделяемой памяти указанного размера с именем название,
в которой хранятся метрики. Имя зоны является узлом в ветке
/status/stream/metric_zones/.
Размер зоны должен составлять не менее восьми системных страниц памяти.
Параметры:
expire=<on|off>— поведение при переполнении зоны:Если
on, самые старые метрики по времени обновления отбрасываются, освобождая память под поступающие;Если
off(по умолчанию) — отбрасываются поступающие метрики, сохраняя информацию об установленных.
discard_key=<название>— задает метрику с ключом название, в которой сохраняются значения отброшенных метрик. По умолчанию такая метрика не создается. Зарезервированный ключ нельзя обновлять вручную.режим — алгоритм обработки значений (см. раздел Режимы работы);
параметры — дополнительная настройка выбранного режима (например,
factorдляaverage exp).
Пример использования:
metric_zone session_time:1m max;
В API дереве шаблон зоны разделяемой памяти выглядит следующим образом:
{
"discarded": 0,
"metrics": {
"ключ1": 123,
"ключ2": 10.5
}
}
Примечание
В зоне размером 1 мегабайт при размере ключа 39 байт и с одним режимом метрики может разместиться около 8 тысяч записей с уникальным ключом.
Режимы работы#
Список доступных режимов работы метрик:
count— счетчик обращений;gauge— стрелка (инкремент/декремент);last— последнее поступившее значение;min— минимальное значение;max— максимальное значение;average exp— скользящее среднее (параметрfactor);average mean— среднее за окно (параметрыwindowиcount);histogram— распределение по "бакетам" (перечень пороговых значений).
В примерах ниже используется конечная точка Каждое обновление метрики увеличивает счетчик на Значение по умолчанию — Примеры: Обновление метрики: Ожидаемое значение метрики в API: Счетчик увеличивает или уменьшает свое значение в зависимости от знака
переданного числа. Положительное значение увеличивает счетчик, отрицательное —
уменьшает. Значение Значение по умолчанию — Примеры: Обновление метрики: Ожидаемое значение метрики в API: Дальнейшие обновления: Ожидаемое значение метрики в API: Хранит последнее полученное значение без какой-либо агрегации.
Если значение опущено, используется Примеры: Обновление метрики: Ожидаемое значение метрики в API: Дальнейшие обновления: Ожидаемое значение метрики в API: Сохраняет минимальное из двух значений — уже сохраненного и нового. Примеры: Обновление метрики: Ожидаемое значение метрики в API: Сохраняет наибольшее из двух значений — уже сохраненного и нового. Примеры: Обновление метрики: Ожидаемое значение метрики в API: Вычисляет среднее значение по алгоритму
экспоненциального сглаживания. Принимает необязательный параметр Чем больше коэффициент, тем сильнее новые значения влияют на среднее.
Если указать Примеры: Обновление метрики: Ожидаемое значение метрики в API: Вычисляет среднее арифметическое. Принимает необязательные параметры
Примечание Например, Примеры: Обновление метрики: Ожидаемое значение метрики в API: Если подождать 5 секунд с момента последнего обновления метрики, то
ожидаемое значение метрики в API: Создает набор "бакетов", увеличивая соответствующий счетчик, если новое
значение не превосходит порога бакета. Формат параметров — список числовых
порогов. Полезен для анализа распределения, например длительности сессий. В качестве обязательных параметров передаются числа — предельные значения
бакетов, обычно перечисляемые в порядке возрастания. Примечание Значение бакета Примеры: Обновление метрики: Ожидаемое значение метрики в API: Дальнейшее обновление: Ожидаемое значение метрики в API: Дальнейшее обновление: Ожидаемое значение метрики в API:/status/ из
примера конфигурации. Результат каждого
примера доступен по адресу /status/stream/metric_zones/<зона>/metrics/.count#
1 независимо от
переданного значения.0.metric_zone count:1m count;
# В качестве составной метрики:
#
# metric_complex_zone count:1m {
# some_metric_name count;
# }
server {
listen 12345;
metric count KEY;
}
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
{
"KEY": 4
}
gauge#
0 не изменяет счетчик.0.metric_zone gauge:1m gauge;
# В качестве составной метрики:
#
# metric_complex_zone gauge:1m {
# some_metric_name gauge;
# }
server {
listen 12351;
metric gauge KEY;
}
server {
listen 12352;
metric gauge KEY=5;
}
server {
listen 12353;
metric gauge KEY=-5;
}
server {
listen 12354;
metric gauge KEY=8;
}
$ nc 127.0.0.1 12351 </dev/null
{
"KEY": 0
}
$ nc 127.0.0.1 12352 </dev/null
$ nc 127.0.0.1 12353 </dev/null
$ nc 127.0.0.1 12354 </dev/null
{
"KEY": 8
}
last#
0.metric_zone last:1m last;
# В качестве составной метрики:
#
# metric_complex_zone last:1m {
# some_metric_name last;
# }
server {
listen 12361;
metric last KEY;
}
server {
listen 12362;
metric last KEY=8000;
}
server {
listen 12363;
metric last KEY=37;
}
server {
listen 12364;
metric last KEY=-3.5;
}
$ nc 127.0.0.1 12361 </dev/null
{
"KEY": 0
}
$ nc 127.0.0.1 12362 </dev/null
$ nc 127.0.0.1 12363 </dev/null
$ nc 127.0.0.1 12364 </dev/null
{
"KEY": -3.5
}
min#
metric_zone min:1m min;
# В качестве составной метрики:
#
# metric_complex_zone min:1m {
# some_metric_name min;
# }
server {
listen 12371;
metric min KEY=42.999;
}
server {
listen 12372;
metric min KEY=-512;
}
server {
listen 12373;
metric min KEY=1;
}
$ nc 127.0.0.1 12371 </dev/null
$ nc 127.0.0.1 12372 </dev/null
$ nc 127.0.0.1 12373 </dev/null
{
"KEY": -512
}
max#
metric_zone max:1m max;
# В качестве составной метрики:
#
# metric_complex_zone max:1m {
# some_metric_name max;
# }
server {
listen 12381;
metric max KEY=42.999;
}
server {
listen 12382;
metric max KEY=-512;
}
server {
listen 12383;
metric max KEY=1;
}
$ nc 127.0.0.1 12381 </dev/null
$ nc 127.0.0.1 12382 </dev/null
$ nc 127.0.0.1 12383 </dev/null
{
"KEY": 42.999
}
average exp#
factor=<число> — коэффициент,
по которому установленное значение учитывается при расчете среднего.
Допустимы целые числа от 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 12391;
metric avg_exp KEY=100;
}
server {
listen 12392;
metric avg_exp KEY=200;
}
server {
listen 12393;
metric avg_exp KEY=0;
}
server {
listen 12394;
metric avg_exp KEY=8;
}
server {
listen 12395;
metric avg_exp KEY=30;
}
$ nc 127.0.0.1 12391 </dev/null
$ nc 127.0.0.1 12392 </dev/null
$ nc 127.0.0.1 12393 </dev/null
$ nc 127.0.0.1 12394 </dev/null
$ nc 127.0.0.1 12395 </dev/null
{
"KEY": 30.16
}
average mean#
window=<off|время> и count=<число>, задающие соответственно
интервал времени и объем выборки для усреднения. По умолчанию:
window=off (учитывается вся выборка) и count=10.window=5s будет учитывать только события последних
5 секунд. Параметр window не может принимать значение 0.
Параметр count=число управляет размером выборки (закэшированных
значений) для более плавного подсчета среднего.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 12401;
metric avg_mean KEY=0.1;
}
server {
listen 12402;
metric avg_mean KEY=0.4;
}
server {
listen 12403;
metric avg_mean KEY=10;
}
server {
listen 12404;
metric avg_mean KEY=1;
}
$ nc 127.0.0.1 12401 </dev/null
$ nc 127.0.0.1 12401 </dev/null
$ nc 127.0.0.1 12402 </dev/null
$ nc 127.0.0.1 12403 </dev/null
$ nc 127.0.0.1 12404 </dev/null
$ nc 127.0.0.1 12404 </dev/null
{
"KEY": 2.1
}
{
"KEY": 0
}
histogram#
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 12411;
metric hist KEY=0.25;
}
server {
listen 12412;
metric hist KEY=2;
}
server {
listen 12413;
metric hist KEY=1000;
}
$ nc 127.0.0.1 12411 </dev/null
{
"KEY": {
"0.1": 0,
"0.2": 0,
"0.5": 1,
"1": 1,
"2": 1,
"inf": 1
}
}
$ nc 127.0.0.1 12412 </dev/null
{
"KEY": {
"0.1": 0,
"0.2": 0,
"0.5": 1,
"1": 1,
"2": 2,
"inf": 2
}
}
$ nc 127.0.0.1 12413 </dev/null
{
"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> для подсчета метрики. Подсчет происходит
в момент установки переменной — например, через директиву set,
которая вычисляется на фазе Pre-access (той же, что и
on=connect), либо программно из модуля
Stream JS, например в обработчике
js_access.
Сеттер принимает ключ с необязательным суффиксом =значение.
Ключ от значения отделяет последний символ =; без суффикса
используется значение 0. Ключ и значение могут состоять из текста,
переменных или их комбинаций. Такие же правила разбора использует директива
metric.
Пример использования:
stream {
metric_zone hits:1m count;
# В этот момент добавилась переменная $metric_hits
server {
listen 12345;
metric hits client=1 on=connect;
}
server {
listen 12346;
set $metric_hits client=1;
return "$metric_hits\n";
}
}
После трех соединений с портом 12345 и одного соединения
с портом 12346:
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12346 </dev/null
client=4
Примечание
Чтение переменной $metric_<name> возвращает ключ вместе с текущим
подсчитанным значением метрики, в виде ключ=значение, а не ту
строку, которая была присвоена буквально. В примере выше буквально
присваивается значение client=1, но поскольку режим count
игнорирует присвоенное значение и просто увеличивает счетчик при каждом
обновлении, чтение переменной $metric_hits после этого возвращает
client=4 — новое значение счетчика, уже учитывающее три
предыдущих обновления с порта 12345.
Также изменится значение, хранящееся в переменной $metric_<name>_key,
на указанный ключ.
$metric_<name>_key и $metric_<name>_value#
Переменные $metric_<name>_key и $metric_<name>_value задают
соответственно ключ и значение. Обновление метрики происходит в момент
установки значения $metric_<name>_value, если ключ в
$metric_<name>_key уже задан.
Примечание
Для составной метрики значения подметрик в переменной
$metric_<name>_value объединены при помощи разделяющего
символа ", ".
Пример использования:
stream {
metric_zone level:1m gauge;
# В этот момент добавились переменные $metric_level, $metric_level_key
# и $metric_level_value
metric_complex_zone stats:1m {
count count;
min min;
avg average exp;
}
# В этот момент добавились переменные $metric_stats, $metric_stats_key
# и $metric_stats_value
server {
listen 12345;
metric level sensor=10 on=connect;
}
server {
listen 12346;
set $metric_level_key "sensor";
set $metric_level_value 5;
# Либо: set $metric_level sensor=5;
return "Updated with '$metric_level'\nValue='$metric_level_value'\n";
}
server {
listen 12347;
set $metric_stats_key bar;
set $metric_stats_value 9;
# Либо: set $metric_stats bar=9;
return "Updated with '$metric_stats'\nValues='$metric_stats_value'\n";
}
}
Для такой конфигурации соединение с портом 12345 (устанавливающее
начальное значение стрелки в 10), за которым следует соединение
с портом 12346, дает:
$ nc 127.0.0.1 12345 </dev/null
$ nc 127.0.0.1 12346 </dev/null
Updated with 'sensor=15'
Value='15'
Соединение с портом 12347 дает:
$ nc 127.0.0.1 12347 </dev/null
Updated with 'bar=1, 9, 9'
Values='1, 9, 9'
Примечание
Если в качестве значения для переменной $metric_<name>_value
указать пустую строку, значение будет распознано как 0. Если
строка состоит из символов, которые невозможно преобразовать в число,
она будет распознана как 1.
Подсчет метрики произойдет только после того, как заданы значения
переменных $metric_<name>_key и $metric_<name>_value.
В таком случае значение, сохраненное в $metric_<name>, станет
равным новой, подсчитанной паре ключ=значение, а не буквально
присвоенным значениям.
Значение, которое хранится в $metric_<name>_key, является
последним указанным через переменные ключом метрики.
Значение, которое хранится в $metric_<name>_value, является
последним подсчитанным значением метрики с ключом, установленным
в $metric_<name>_key.
$metric_<name>_value_<metric>#
Для составной метрики значение конкретной подметрики можно получить при
помощи переменной $metric_<name>_value_<metric>, указав имя
подметрики в качестве <metric>.
Пример использования:
stream {
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 12345;
set $metric_foo_key bar;
set $metric_foo_value 9;
# Либо: set $metric_foo bar=9;
return "Updated with '$metric_foo'\nValues='$metric_foo_value'\nCount='$metric_foo_value_count'\n";
}
}
Для такой конфигурации соединение с портом 12345 дает:
$ nc 127.0.0.1 12345 </dev/null
Updated with 'bar=1, 9, 9'
Values='1, 9, 9'
Count='1'
Дополнительные примеры#
Мониторинг соединений по протоколу#
metric_zone protocols:1m count;
server {
listen 12345;
listen 12346 udp;
metric protocols $protocol;
}
Ответ:
{
"TCP": 340,
"UDP": 58
}
Распределение времени сессии upstream#
metric_zone upstream_time:10m expire=on histogram
0.05 0.1 0.3 0.5 1 2 5 10 inf;
server {
listen 12345;
proxy_pass backend;
metric upstream_time $upstream_addr=$upstream_session_time on=end;
}
Ответ:
{
"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
}
}
}
Активные подключения#
stream {
metric_zone active_connections:2m gauge;
server {
listen 12345;
metric active_connections service_a=1 on=connect;
metric active_connections service_a=-1 on=end;
}
server {
listen 12346;
metric active_connections service_b=1 on=connect;
metric active_connections service_b=-1 on=end;
}
}
http {
server {
listen 8080;
location /connections/ {
allow 127.0.0.1;
deny all;
api /status/stream/metric_zones/active_connections/metrics/;
}
}
}
Ответ:
{
"service_a": 42,
"service_b": 17
}
Поддержка Prometheus#
В Angie есть встроенный модуль Prometheus для
отображения метрик
в формате Prometheus,
который поддерживает произвольные метрики, включая метрики, собранные на
потоковой стороне. Поскольку экспорт в формате Prometheus — HTTP-функция,
директивы prometheus_template и
prometheus задаются в блоке http, обращаясь к
потоковой зоне метрики через единое дерево API /status/.
В качестве примера интеграции рассмотрим следующую конфигурацию:
stream {
# Создание метрики "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;
}
upstream backend {
server 127.0.0.1:15001;
}
server {
listen 12345;
# Обновление метрики по завершении сессии общим количеством
# байт, полученных от клиента
proxy_pass backend;
metric upload angie=$bytes_received on=end;
}
}
http {
# Описание шаблона Prometheus для метрики "upload"
prometheus_template upload_metric {
'stats{le="$1"}' $p8s_value
path=~^/stream/metric_zones/upload/metrics/angie/stats/(.+)$
type=histogram;
'stats_sum' $p8s_value
path=/stream/metric_zones/upload/metrics/angie/sum;
'stats_count' $p8s_value
path=/stream/metric_zones/upload/metrics/angie/count;
'avg_size' $p8s_value
path=/stream/metric_zones/upload/metrics/angie/avg_size;
}
server {
listen 80;
# Таргет для сбора метрик
location /prometheus/upload_metric/ {
prometheus upload_metric;
}
}
}
После пяти соединений с портом 12345, при которых были отправлены
данные размером 16384, 64448, 64, 1028
и 1028 байт:
$ head -c 16384 /dev/urandom | nc 127.0.0.1 12345
$ head -c 64448 /dev/urandom | nc 127.0.0.1 12345
$ head -c 64 /dev/urandom | nc 127.0.0.1 12345
$ head -c 1028 /dev/urandom | nc 127.0.0.1 12345
$ head -c 1028 /dev/urandom | nc 127.0.0.1 12345
Значения метрики будут следующими:
{
"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