Как мы измеряем задержку
Мониторы PingZen показывают latency для проверок Ping и HTTP/HTTPS. Ниже описано как именно мы измеряем, что значит каждая метрика, и как наши цифры соотносятся с индустриальными инструментами (Linux ping, curl, icmplib, fping).
PING (ICMP) — kernel-accurate RTT
Для Ping-мониторов мы запускаем системный ping как subprocess (так же делают Datadog и большинство open-source пробников) и парсим его статистику. Все значения RTT приходят напрямую из ядра — не из Python-таймера — поэтому они идентичны тому, что вы увидите, запустив ping на хосте пробника сами.
Как именно мы меряем (пошагово)
- Запускаем
ping -c N -i 0.2 <host>через asyncio subprocess (N — настраиваемое число пакетов, по умолчанию 4). - iputils
pingиспользуетgettimeofday()для send-side timestamp + kernelSO_TIMESTAMPcontrol message (cmsg) для receive-side — см. исходники iputils. - Парсим строку
rtt min/avg/max/mdevиз stdout (Linux) илиround-trip min/avg/max/stddev(macOS). - Сохраняем min_rtt, avg_rtt, max_rtt, jitter (mdev) и packet_loss как float-значения в таблице
check_results. - Целочисленная колонка
response_time_ms=round(avg_rtt)с минимальным значением 1 мс — sub-ms RTT округляются до 1 мс вместо 0 (0 мс читается как «нет ответа»). - Overhead fork+exec (~10 мс wall-clock) исключается — он никогда не попадает в RTT-значения.
Поля в базе
ping_avg_rtt
Средний RTT в миллисекундах (float, 3 знака после запятой). Kernel-measured. Используйте для sub-ms точности — например, для целей внутри своего ДЦ значение будет 0.260 мс.
ping_min_rtt / ping_max_rtt
Минимальный и максимальный RTT среди всех эхо-пакетов в одной проверке (float).
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 напрямую.
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.
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 ICMP | ping subprocess + парсинг stdout | Идентично PingZen |
| Prometheus blackbox_exporter | Go time.Now() userspace-timestamps, без SO_TIMESTAMPING | PingZen имеет тот же класс точности через kernel recv-side cmsg |
| SmokePing / fping | Batched C raw sockets для эффективности | Те же метрики (min/avg/max/jitter/loss), другая реализация |
| RIPE Atlas | ICMP echo со встроенным firmware-timestamp | Точность публично не задокументирована |
| icmplib (Python) | Чистый Python raw-socket | Валидирован против PingZen subprocess — RTT совпадает до 0.1 мс |
| SO_TIMESTAMPING | Kernel 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 мс.