Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 2 из 25 ~18 мин +20 XP

Четыре типа метрик: Counter, Gauge, Histogram, Summary

Типы метрик Prometheus — первое решение, которое принимается до строчки кода: Counter, Gauge, Histogram и Summary нельзя поменять местами задним числом, а неверный выбор рисует ложные графики. Разберём, за что отвечает каждый тип и как выбирать без ошибок.

Содержание урока · 10 разделов

Владелец магазина хочет знать две вещи: сколько всего заказов создано и сколько прямо сейчас висит в очереди на обработку. Числа похожи, но живут по разным законам: первое только растёт, второе ходит вверх и вниз. Если мерить оба одной метрикой, одна из двух историй будет врать. В Prometheus за эти два закона отвечают разные типы, и всего их четыре: Counter, Gauge, Histogram и Summary. Тип выбирается один раз: он вшивается в формат экспозиции — строку TYPE и суффиксы имён, — поэтому «сменить тип» на живой метрике нельзя; по факту вы заведёте новую метрику, а старые дашборды и алерты замолчат. Поэтому этот урок — про выбор, а код с типами Counter и Gauge пойдёт следом: в следующих двух уроках каждый из них получит свой разбор со всеми методами и ловушками.

Типы метрик Prometheus: карта из четырёх сущностей#

Counter — счётчик, который может только расти и сбрасывается в ноль при рестарте процесса: запросы, ошибки, обработанные байты. Gauge — датчик текущего значения, который двигается в обе стороны: длина очереди, занятая память, активные сессии. Histogram — распределение: каждое измерение падает в заранее заданные бакеты, из которых уже на стороне Prometheus считаются доли и перцентили — про латентность и размеры. Summary — второй способ считать квантили, но на стороне клиента: сервис сам агрегирует наблюдения и отдаёт готовые квантильные значения. Первые два типа — про «сколько всего» и «сколько сейчас», третий и четвёртый — про «как распределено». Типичный сервис на Go большую часть времени живёт на Counter и Histogram: рост и латентность — главные сигналы здоровья; Gauge добавляется под очереди и пулы, Summary — точечно. Дальше по карте — два самых ходовых типа в деталях, а гистограммы с summary получат собственные уроки после темы лейблов.

Counter и Gauge: счётчик против датчика#

Разницу удобно держать в голове через пару «пробег автомобиля» и «спидометр». Counter — пробег: число с запуска, убывать не может физически, даже когда события закончились, значение просто стоит на месте. Gauge — спидометр: сейчас сто двадцать, через минуту ноль, и оба показания честные. Отсюда рабочая проверка при выборе типа: спросите себя, что означает ноль. Для счётчика ошибок ноль — «с запуска ошибок не было», нормально. Для числа активных пользователей ноль — «прямо сейчас никто не онлайн», тоже нормально, но смысл другой: это состояние, а не накопление. Самая живая ловушка — рестарт: счётчик обнуляется, датчик начинает с того, что вы в него записали. Prometheus знает про поведение счётчика и в запросах умеет склеивать сбросы; датчику склейка не нужна — у него нет истории, есть момент. Итого: растущее — Counter, текущее состояние — Gauge, и никогда наоборот.

Типичная ошибка: сырой счётчик на графике#

Ошибка интерпретации, на которой я ловлю почти каждого новичка. Счётчик запросов рисуют сырым значением: линия ползёт вверх, после деплоя падает в ноль и снова растёт. Человек, привыкший к датчикам, читает это как «трафик упал до нуля и восстановился» — и начинается поиск аварии, которой не было. На деле провал в ноль — рестарт процесса, а скорость по такой линии на глаз не оценить: рост с тысячи до двух тысяч за сутки и за год выглядит одинаково круто. Правильная картинка — прирост: в PromQL это rate, который сглаживает и сбросы, и рваный ритм событий. Пока PromQL не изучен, держите простое правило: сырое значение счётчика годится для лога, для графика — только скорость. Тот же принцип встретите у гистограмм: сырые бакеты там тоже никто не рисует, рисуют доли и квантили.

Два типа рядом: как это выглядит в Go

GO
package main

import (
        "github.com/prometheus/client_golang/prometheus"
)

var (
        orders = prometheus.NewCounter(prometheus.CounterOpts{
                Name: "shop_orders_total",
                Help: "Всего созданных заказов.",
        })
        queue = prometheus.NewGauge(prometheus.GaugeOpts{
                Name: "shop_queue_size",
                Help: "Текущая длина очереди заказов.",
        })
)

func main() {
        prometheus.MustRegister(orders, queue)

        orders.Inc()   // +1 заказ
        queue.Set(17)  // в очереди сейчас 17
        queue.Dec()    // один заказ обработан
}

Counter создаётся через prometheus.NewCounter с CounterOpts, Gauge — через NewGauge с GaugeOpts; у обоих обязательны Name и Help. Методы разные по духу: у счётчика только Inc и Add с неотрицательным аргументом, у датчика — Set, Inc, Dec и Add с любым знаком. MustRegister регистрирует обе метрики в реестре по умолчанию. Полный разбор каждого типа — в двух следующих уроках.

Четыре типа глазами формата экспозиции

GO
# HELP shop_orders_total Всего созданных заказов.
# TYPE shop_orders_total counter
shop_orders_total 812

# TYPE shop_queue_size gauge
shop_queue_size 17

# TYPE shop_request_duration_seconds histogram
shop_request_duration_seconds_bucket{le="0.1"} 240
shop_request_duration_seconds_bucket{le="0.5"} 380
shop_request_duration_seconds_bucket{le="+Inf"} 400
shop_request_duration_seconds_sum 92.1
shop_request_duration_seconds_count 400

Тип вшит в экспозицию: у counter суффикс _total, у gauge одна строка значения, у histogram — семейство _bucket с меткой le плюс _sum и _count. Строка TYPE объявляет тип до строк значений. Как устроены бакеты и метка le — разбор в седьмом уроке про гистограммы; summary вместо бакетов покажет строки вида quantile.

Четыре типа метрик: сравнение по конструкции#

ТипЗакон измененияЧто меритьКлючевые методы в Go
Counterтолько вверх, сброс при рестартезапросы, ошибки, байтыInc, Add с неотрицательным
Gaugeвверх и внизочередь, память, сессииSet, Inc, Dec, Add
Histogramнаблюдения в бакетылатентность, размер ответаObserve
Summaryквантили на клиентеперцентили одного процессаObserve

Histogram: латентность по бакетам#

Гистограмма не хранит каждое измерение — это разорвало бы кардинальность. Вместо этого вы заранее задаёте бакеты, пороги времени: сто миллисекунд, двести, пятьсот, секунда, две, пять. Каждое наблюдение увеличивает счётчики бакетов, чей порог превышен: ответ за триста миллисекунд добавит единицу в бакеты пятьсот, секунду, две и пять. Плюс копятся сумма и число наблюдений. Красота схемы в том, что клиентская часть дёшево стоит, а математика происходит в Prometheus: из накопленных бакетов считается, какая доля запросов быстрее порога, и оцениваются перцентили — медиана и девяносто девятый. Цена — приближённость: значение p99 вычисляется по границам бакетов, и при плохом выборе порогов оценка плывёт. Зато гистограммы агрегируются между инстансами: суммировать бакеты двадцати подов — нормальная операция. Это делает histogram выбором по умолчанию для латентности HTTP — в седьмом уроке соберём её руками и подберём бакеты.

Summary: квантили на стороне клиента#

Summary решает ту же задачу, что гистограмма, но с другой стороны: сервис сам держит скользящее окно наблюдений и отдаёт готовые квантили — скажем, медиану и девяносто девятый перцентиль — просто числами. Плюс честный: значение квантиля точнее, чем оценка по бакетам, и пороги угадывать не надо. Минус один и жирный: квантили нельзя агрегировать. Медиана двух подов — не сумма медиан, и Prometheus не может собрать p99 по кластеру из summary двадцати инстансов; каждая метрика замыкается на своём процессе. Плюс окно квантилей зашито в конфигурацию клиента и не меняется на лету. Поэтому в типичном Go-сервисе summary занимает нишу точечных метрик одного процесса — скажем, латентность внутреннего вызова конкретного инстанса, — а для HTTP-латентности и всего, что надо смотреть по кластеру, выбирают histogram. Сравнение с кодом и выводами — в восьмом уроке, где оба типа лягут рядом.

Спорное мнение: summary почти всегда лишний#

Спорное мнение, к которому я пришёл после пары лет с квантилями: в прикладном Go-сервисе summary почти не нужен. Его честные аргументы — точный квантиль и отсутствие подбора сетки — звучат весомо, пока сервис один. Но практически каждый сервис, доживший до успеха, обрастает вторым инстансом: горизонтальное масштабирование, выкатка в два региона, canary-релиз. С этого момента клиентские квантили замыкаются в себе, рядом всё равно появляется гистограмма, и два типа для одной величины содержать дороже, чем один. Граница, при которой я меняю мнение: фоновый процесс без горизонтального масштабирования и с тяжёлым, плохо предсказуемым хвостом — там summary с одной целью и окном MaxAge действительно проще, чем угадывать бакеты. Встроенный go_gc_duration_seconds к спору не относится: его писали не мы, и убрать его из реестра никто не просил.

Проверка: выбор типа

Нужно мерить число задач, которые прямо сейчас ждут обработки в очереди. Какой тип выбираете?

Проверка: закон Counter

Что произойдёт со значением Counter после перезапуска процесса?

Что покажет датчик после трёх операций

Прочитайте код и выберите, что он выведет:

queue.Set(42)
queue.Inc()
queue.Inc()
queue.Dec()

Законный и незаконный Add

Прочитайте код и выберите, что он выведет:

orders.Add(0.5)  // законно?
queue.Add(-3)    // законно?

Восстановите пару счётчик-датчик

Допишите типы в служебных строках экспозиции: у первой метрики счётчик, у второй — датчик.

# HELP shop_orders_total Всего созданных заказов. # TYPE shop_orders_total shop_orders_total 812 # HELP shop_queue_size Текущая длина очереди. # TYPE shop_queue_size shop_queue_size 17

Соберите экспозицию двух метрик

Расставьте строки экспозиции в правильном порядке: у каждой метрики сначала описание, потом тип, потом значение.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: типы метрик

Термин

Counter

нажмите, чтобы перевернуть
Определение

Метрика только вверх: запросы, ошибки, байты. Методы Inc и Add с неотрицательным аргументом; при рестарте обнуляется, Prometheus склеивает сбросы в rate().

1 / 6

Карта типов у вас в голове — время потрогать её руками. В следующем уроке ставим client_golang: go get, promhttp.Handler() и первая страница /metrics с бесплатными Go-метриками. Потом два урока-разбора: счётчик со всеми методами и ловушками, включая обнуление при рестарте, и датчик с Set, Inc и Dec и правилом, почему растущее нельзя мерить gauge-ом. Гистограммы и summary получат свои уроки после лейблов. Примеры запускаются локальным Go: песочница не поднимает HTTP-сервер, поэтому держите под рукой терминал. Если ваш сервис уже живёт на фреймворке, всё то же самое работает и там — например, в сервисе на gin счётчики встают рядом с middleware без правок маршрутов; карта всех двадцати пяти тем — на странице курса.

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • Типов четыре: Counter растёт, Gauge живёт состоянием, Histogram копит бакеты, Summary считает квантили на клиенте.
  • Тип вшивается в экспозицию и имена — смена типа равнозначна заведению новой метрики.
  • Проверка выбора: ноль у счётчика — «не было с запуска», у датчика — «пусто сейчас».
  • Histogram агрегируется между инстансами и оценивает p99 по бакетам; summary точнее, но не агрегируется.
  • В Go: NewCounter и NewGauge с Name и Help, регистрация MustRegister, дальше Inc и Add против Set, Inc, Dec.
  • Типичный сервис живёт на Counter и Histogram; Gauge — под очереди и пулы, Summary — точечно.

Домашнее задание

Выберите любой сервис, который знаете: выпишите пять метрик, которые вы завели бы в первый день, и тип каждой. Особое внимание — метрикам, которые хочется мерить счётчиком, хотя они убывают: это самая частая ошибка выбора. Сверьте свои ответы с таблицей из урока; спорные случаи пригодятся на уроке про лейблы.

Личные заметкиПусто
Хранятся только в вашем браузере (Prometheus · Четыре типа метрик: Counter, Gauge, Histogram, Summary)

Вопросы и ответы по уроку

Какой тип выбрать для времени ответа API?

Histogram по умолчанию: бакеты дёшевы на клиенте, а перцентили считаются на стороне Prometheus и агрегируются по кластеру. Summary имеет смысл для точечных метрик одного процесса, где агрегация не нужна.

Можно ли измерять деньги счётчиком?

Да, Counter.Add принимает дроби: сумма выручки — классическая бизнес-метрика с суффиксом вроде _total. Важно лишь, чтобы значение не убывало: возвраты заводят отдельной метрикой, а не отрицательным Add.

Что будет, если перепутать типы?

Графики либо бессмысленны, либо лгут: растущая величина в gauge не поддаётся анализу скорости, убывающая в counter паникует при попытке уменьшения. Тип меняется только заводом новой метрики.

Термины урока

Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.

Следующая ступень учебного пути

WebSocket25 уроков~13 чпрактика25 уроков уже доступны

WebSocket в Go: real-time чаты и уведомления

Курс WebSocket на Go: gorilla/websocket, эхо-сервер, hub-паттерн, комнаты, авторизация соединений, ping/pong, reconnect и масштабирование. 25 уроков.

  • Поднимать WS-сервер и апгрейдить HTTP-соединения
  • Писать readPump/writePump без гонок на каждое соединение
  • Строить hub: регистрацию, рассылку и комнаты
  • Авторизовывать соединения и ограничивать сообщения