Перейти к основному содержимому

Как мы измеряем задержку

Мониторы PingZen показывают latency для проверок Ping и HTTP/HTTPS. Ниже описано как именно мы измеряем, что значит каждая метрика, и как наши цифры соотносятся с индустриальными инструментами (Linux ping, curl, icmplib, fping).

PING (ICMP) — kernel-accurate RTT

Для Ping-мониторов мы запускаем системный ping как subprocess (так же делают Datadog и большинство open-source пробников) и парсим его статистику. Все значения RTT приходят напрямую из ядра — не из Python-таймера — поэтому они идентичны тому, что вы увидите, запустив ping на хосте пробника сами.

Как именно мы меряем (пошагово)

  1. Запускаем ping -c N -i 0.2 <host> через asyncio subprocess (N — настраиваемое число пакетов, по умолчанию 4).
  2. iputils ping использует gettimeofday() для send-side timestamp + kernel SO_TIMESTAMP control message (cmsg) для receive-side — см. исходники iputils.
  3. Парсим строку rtt min/avg/max/mdev из stdout (Linux) или round-trip min/avg/max/stddev (macOS).
  4. Сохраняем min_rtt, avg_rtt, max_rtt, jitter (mdev) и packet_loss как float-значения в таблице check_results.
  5. Целочисленная колонка response_time_ms = round(avg_rtt) с минимальным значением 1 мс — sub-ms RTT округляются до 1 мс вместо 0 (0 мс читается как «нет ответа»).
  6. Overhead fork+exec (~10 мс wall-clock) исключается — он никогда не попадает в RTT-значения.
fork+execRTTping_avg_rtt

Поля в базе

ping_avg_rtt

Средний RTT в миллисекундах (float, 3 знака после запятой). Kernel-measured. Используйте для sub-ms точности — например, для целей внутри своего ДЦ значение будет 0.260 мс.

ping_min_rtt / ping_max_rtt

Минимальный и максимальный RTT среди всех эхо-пакетов в одной проверке (float).

minmax

ping_jitter

Среднее отклонение RTT от среднего (mdev) по данным Linux ping. Важно: это классический mdev, а не RFC 3550 inter-arrival jitter, используемый в RTP-инструментах — формулы разные.

ping_packet_loss

Доля пакетов без ответа в пределах таймаута (0.0–100.0 %).

response_time_ms

Целые миллисекунды — равно round(ping_avg_rtt) с минимумом 1 мс. Основная метрика UI; для sub-ms используйте ping_avg_rtt напрямую.

ping_avg_rtt 0.260response_time_ms 1

Sub-ms цели (внутри одного ДЦ)

Если ваш пробник и цель в одном датацентре (типично для bare-metal), реальный RTT 0.2–0.9 мс. Целочисленное response_time_ms округляется вниз с минимумом 1 мс, чтобы не показывать «0ms» (это выглядит как неудачная проверка). Истинное sub-ms значение всегда сохранено в float-поле ping_avg_rtt — видно в API check_results и во всплывающей подсказке графика.

HTTP / HTTPS — Time to First Byte с разбивкой

Для HTTP/HTTPS-мониторов мы используем httpx.AsyncClient с trace-хуком, захватывающим метки по фазам. Пишем полное wall-clock время + 4 суб-события. Соответствует curl time_total и time_starttransfer.

response_time_ms 24 mstcptlsttfbserver 6 ms

Timing-поля на каждую проверку

response_time_ms (total)

Полный wall-clock от построения запроса до возврата из await response. Соответствует curl time_total без чтения тела. Включает TCP + TLS + TTFB + ~5–15 мс Python / httpx / asyncio overhead (поиск в пуле, создание Response-объекта, планирование event-loop). Это «то, что увидел клиент».

timing_tcp_ms

TCP handshake: connection.connect_tcp.started → connection.connect_tcp.complete. Эквивалент curl time_connect - time_namelookup. Один RTT в типичных условиях.

timing_tls_ms

TLS handshake: connection.start_tls.started → connection.start_tls.complete. Эквивалент curl time_appconnect - time_connect. Один RTT для TLS 1.3, ~2 RTT для TLS 1.2.

timing_ttfb_ms

Request-send → первый байт ответа: http11.send_request_headers.started → http11.receive_response_headers.started. Чисто серверная обработка + один RTT. Эквивалент curl time_starttransfer - time_pretransfer.

(не сохраняется) DNS, чтение тела

DNS обычно закеширован пулом httpx и не триггерит отдельный trace-event. Чтение тела не включено в total — мы останавливаемся перед response.content. При необходимости добавить — через Server-Timing header.

Почему total > tcp + tls + ttfb

При warm-reuse соединения sub-events могут вообще не сработать (соединение уже открыто), но total всё равно будет 5–20 мс. Разница — реальный Python / httpx / asyncio overhead: auth и построение заголовков до первого trace-event, создание Response и закрытие после последнего event, плюс event-loop scheduling под нагрузкой. Это нормально для любого Python-based HTTP-мониторинга — curl показывает такой же 2–8 мс gap между суммой phases и total. Поля timing_* дают чистую картину network / server.

Индустриальные стандарты, которым мы соответствуем

Измерения PingZen RFC-compliant и совпадают с крупными инструментами:

Инструмент или стандартКак меряетОтношение к PingZen
RFC 2681 (IPPM)Round-trip Delay MetricНаш ICMP-путь через системный ping квалифицируется как Type-P-Round-trip-Delay
Datadog Synthetic ICMPping subprocess + парсинг stdoutИдентично PingZen
Prometheus blackbox_exporterGo time.Now() userspace-timestamps, без SO_TIMESTAMPINGPingZen имеет тот же класс точности через kernel recv-side cmsg
SmokePing / fpingBatched C raw sockets для эффективностиТе же метрики (min/avg/max/jitter/loss), другая реализация
RIPE AtlasICMP echo со встроенным firmware-timestampТочность публично не задокументирована
icmplib (Python)Чистый Python raw-socketВалидирован против PingZen subprocess — RTT совпадает до 0.1 мс
SO_TIMESTAMPINGKernel hardware timestamps, наносекундная точностьИзбыточно для application-level uptime-мониторинга и не поддерживается в контейнерах

Частые вопросы

Мой роутер показывает sub-ms ping, а PingZen — 30+ мс. Почему?

Скорее всего, разные цели на разных маршрутах. Запустите ping <target_ip> на хосте, где крутится пробник PingZen — почти всегда получите то же значение, что мы показываем. Если не совпадает — пришлите вывод, разберёмся с маршрутами.

HTTP response_time = 24 мс, а мой сервер репортит 6 мс обработки. Где потерянные 18 мс?

Total — это wall-clock с момента инициализации клиента; ваши 6 мс — чисто обработка. Разница включает TCP ACK propagation, поиск в httpx-пуле, создание Python-объектов, event-loop scheduling. Для чистой серверной latency смотрите timing_ttfb_ms (уже исключает TCP и TLS).

Можно экспортировать float ping_avg_rtt?

Да — API check_results возвращает ping_avg_rtt как float. Tooltip графиков показывает его с 3 знаками. Для sub-ms in-DC пингов это единственное поле, которое показывает реальное значение.

Почему jitter (mdev) иногда выше ожидаемого?

Мы показываем Linux ping mdev (mean deviation of RTTs from average), а не RFC 3550 inter-arrival jitter, используемый в RTP-инструментах. Оба валидны, но меряют разное. На стабильных внутри-ДЦ линках mdev обычно 0.01–0.5 мс.