1.1 Путь HTTP-запроса: DNS → TCP → TLS → HTTP
Зачем это аналитику
Когда в требованиях пишут «таймаут 5 секунд», надо понимать, из чего складываются эти секунды: резолв имени, установка соединения, рукопожатие, ожидание ответа, передача тела. Без этого невозможно осмысленно разбирать инциденты вида «интеграция тормозит» и обсуждать с разработкой connect timeout против read timeout, пулы соединений и keep-alive.
Готовность к модулю
Модуль опирается на темы ниже. Если какая-то из них незнакома или её вопросы для самопроверки даются с трудом, начните с неё.
Что понять
- DNS: рекурсивный резолвер, TTL, кэширование на разных уровнях. Почему после смены IP партнёра «у одних работает, у других нет».
- TCP: трёхстороннее рукопожатие (SYN, SYN-ACK, ACK) — это один RTT до первого байта данных. Что такое RTT и почему расстояние до дата-центра важнее пропускной способности канала.
- TLS-рукопожатие: в TLS 1.3 это ещё +1 RTT (в TLS 1.2 было +2). Возобновление сессии и 0-RTT, и почему 0-RTT небезопасен для неидемпотентных запросов (replay).
- HTTP/1.1: keep-alive и пул соединений, head-of-line blocking. Почему браузеры держат по 6 соединений на хост.
- HTTP/2: мультиплексирование потоков в одном соединении, бинарные фреймы, сжатие заголовков HPACK. HOL-блокировка переехала с уровня HTTP на уровень TCP.
- HTTP/3 и QUIC: транспорт поверх UDP, TLS встроен в рукопожатие, нет HOL-блокировки на уровне транспорта, миграция соединения при смене сети.
- Таймауты: connect timeout, TLS handshake timeout, read / idle timeout, общий дедлайн запроса. Кто из них что меряет.
- Прокси и промежуточные узлы: forward proxy, reverse proxy, где терминируется TLS, заголовки
X-Forwarded-For/Forwarded, и почему сервер видит IP балансировщика, а не клиента.
Урок
Из чего складывается время запроса
Когда сервис вызывает https://api.partner.example/orders, до первого байта HTTP-ответа проходит несколько независимых этапов: найти IP-адрес по имени, установить TCP-соединение, договориться о шифровании, отправить запрос и дождаться, пока сервер его обработает. Почти каждый этап стоит одного или нескольких полных оборотов пакета туда и обратно — это и есть RTT (round-trip time).
Главная мысль урока: для типичного API-вызова с телом в несколько килобайт время определяется не пропускной способностью канала, а числом RTT и временем обработки на сервере. Канал в 1 Гбит/с не ускорит рукопожатие ни на миллисекунду, если сервер в другом полушарии.
sequenceDiagram
participant C as Клиент
participant R as DNS-резолвер
participant S as Сервер
C->>R: Запрос A/AAAA для api.partner.example
R-->>C: IP-адрес (из кэша или после обхода иерархии)
C->>S: TCP SYN
S-->>C: TCP SYN-ACK
C->>S: ACK и TLS ClientHello с ключевым материалом
S-->>C: ServerHello, сертификат, Finished
C->>S: TLS Finished и HTTP-запрос
Note over S: Обработка на сервере
S-->>C: HTTP-ответ
На схеме — новое соединение по TLS 1.3. Дальше разберём каждый этап и то, как его избегают при повторных запросах.
DNS: имя, кэши и TTL
Приложение редко само ходит к авторитетным серверам. Цепочка обычно такая: библиотека в процессе обращается к системному резолверу-заглушке (stub resolver), тот — к рекурсивному резолверу (провайдера, корпоративному, публичному или облачному). Рекурсивный резолвер, если ответа нет в кэше, проходит иерархию: корневые серверы сообщают, кто отвечает за зону .example, те — кто отвечает за partner.example, и уже авторитетный сервер этой зоны возвращает запись A (IPv4) или AAAA (IPv6). Команда dig +trace показывает ровно этот обход.
Каждый ответ несёт TTL — сколько секунд его можно хранить. Кэшируют на многих уровнях сразу:
- рекурсивный резолвер;
- операционная система (на части систем есть локальный кэширующий демон);
- браузер со своим кэшем имён;
- рантайм языка: JVM, например, кэширует результаты резолва сама, и при некоторых настройках — надолго;
- пул соединений: открытое TCP-соединение вообще не требует DNS, оно привязано к IP, пока живёт.
Отсюда классическая ситуация «у одних работает, у других нет» после того, как партнёр сменил IP. Резолверы получили старую запись в разное время, и каждый держит её до истечения своего TTL. Некоторые резолверы и клиенты не соблюдают TTL строго. Сервис, у которого соединения в пуле живут часами, продолжит ходить на старый адрес, пока соединение не оборвётся. А если старый адрес партнёр не выключил, часть трафика тихо идёт на старую площадку.
Практические следствия для требований к интеграции:
- Партнёр должен заранее снижать TTL перед переездом, а после переезда какое-то время держать старый адрес рабочим.
- Если партнёр требует белый список IP (частая история в банковских интеграциях), смена адреса с любой стороны — это плановое изменение с согласованием, а не «поправим DNS».
- Поиск по имени — тоже сетевой вызов с задержкой и отказами. Недоступный резолвер выглядит для приложения как зависание до таймаута, а не как ошибка сервиса партнёра.
Отдельно стоит помнить про отрицательное кэширование: ответ «такого имени нет» тоже кэшируется. Если запись создали позже, чем её кто-то запросил, этот кто-то какое-то время будет получать ошибку.
TCP: одно рукопожатие и физика
Прежде чем передать данные, TCP устанавливает соединение: клиент отправляет SYN, сервер отвечает SYN-ACK, клиент отправляет ACK. Данные клиент может отправить вместе с ACK или сразу после него, поэтому рукопожатие стоит один RTT до того, как запрос вообще уйдёт.
Нижнюю границу RTT задаёт физика. Свет в оптоволокне проходит примерно 200 тысяч километров в секунду, то есть каждые 100 км маршрута добавляют к RTT около миллисекунды. Реальный маршрут длиннее прямой линии, добавляются маршрутизаторы и очереди, поэтому фактический RTT между странами — десятки миллисекунд, между континентами — порядка сотни и больше. Никакая «толстая труба» это не исправит.
Вторая причина, по которой полоса вторична, — медленный старт (slow start). Новое TCP-соединение не знает ёмкость канала и начинает с небольшого окна перегрузки (congestion window, в современных стеках порядка десяти сегментов, около 14 КБ). Окно растёт с каждым подтверждённым RTT. Ответ на сотни килобайт по новому соединению потребует нескольких оборотов, даже если канал широкий. Прогретое соединение из пула уже прошло эту фазу — ещё один аргумент за переиспользование соединений.
Полезная для инцидентов деталь: если порт закрыт, сервер отвечает RST, и клиент сразу получает «connection refused». Если пакеты молча отбрасывает межсетевой экран, клиент ждёт до срабатывания connect timeout. Мгновенная ошибка и зависание на N секунд — разные диагнозы.
TLS: ещё один или два RTT
Поверх TCP идёт рукопожатие TLS: стороны согласуют версию и шифры, сервер предъявляет сертификат, обе стороны вырабатывают общий ключ. Подробно механизм разбирается в модуле 1.2, здесь важна стоимость.
- TLS 1.2 — полное рукопожатие занимает 2 RTT.
- TLS 1.3 — 1 RTT. Клиент в первом же сообщении отправляет свою часть обмена ключами (key share), угадывая группу, которую сервер почти наверняка поддерживает.
Возобновление сессии (session resumption) позволяет не повторять полную процедуру: при первом соединении сервер выдаёт клиенту билет (session ticket) или идентификатор, а при следующем стороны используют ранее выработанный секрет. Это экономит вычисления и проверку сертификата, но в TLS 1.3 обычное возобновление всё равно стоит 1 RTT.
Режим 0-RTT (early data) в TLS 1.3 идёт дальше: при возобновлении клиент отправляет данные приложения прямо в первом пакете, не дожидаясь ответа сервера. Цена — защита от повтора (replay). Злоумышленник, перехвативший этот первый пакет, может переслать его серверу ещё раз, и сервер, не имея свежего вклада в ключ от текущего обмена, не может надёжно отличить повтор от оригинала. Для GET /catalog повтор безвреден. Для POST /payments повтор — второй платёж. Поэтому серверы и балансировщики принимают early data только для безопасных, идемпотентных запросов, а для HTTP есть механизм отказа: заголовок Early-Data и код 425 Too Early, по которому клиент повторяет запрос уже после полного рукопожатия. В серверных B2B-интеграциях 0-RTT обычно просто выключен.
Переговоры о версии HTTP идут внутри того же рукопожатия через расширение ALPN (Application-Layer Protocol Negotiation): клиент перечисляет, что умеет (h2, http/1.1), сервер выбирает. Лишнего RTT на это не тратится.
Сколько RTT до первого байта
Сведём всё вместе. Время DNS не учтено: при попадании в кэш оно близко к нулю, при промахе добавляет свои обороты до резолвера и дальше. Последний RTT в каждой строке — сам HTTP-запрос и ответ, к нему прибавляется время обработки на сервере.
| Сценарий | Рукопожатия | RTT до первого байта ответа |
|---|---|---|
| Новое соединение, TLS 1.2 | TCP 1 + TLS 2 | 4 |
| Новое соединение, TLS 1.3 | TCP 1 + TLS 1 | 3 |
| Новое соединение, HTTP/3 (QUIC) | QUIC с TLS 1 | 2 |
| HTTP/3 с 0-RTT при возобновлении | нет | 1 |
| Переиспользованное соединение (любая версия) | нет | 1 |
Последняя строка объясняет, почему пул соединений и keep-alive важнее выбора версии протокола для межсервисных вызовов: прогретое соединение сводит сетевую часть к одному RTT.
HTTP/1.1: keep-alive и очередь на соединении
В HTTP/1.1 соединения по умолчанию постоянные (persistent, keep-alive): после ответа соединение не закрывается и может использоваться для следующего запроса. HTTP-клиенты в приложениях держат пул таких соединений на каждый хост.
Ограничение HTTP/1.1 в том, что на одном соединении в каждый момент выполняется один запрос: следующий ждёт, пока полностью придёт предыдущий ответ. Конвейерная отправка (pipelining) в спецификации есть, но ответы всё равно должны идти строго по порядку, и на практике её почти не используют. Медленный первый ответ задерживает все за ним — это блокировка начала очереди (head-of-line blocking, HOL).
Обходной путь — открыть несколько соединений параллельно. Браузеры договорились о компромиссе: около шести соединений на хост. Больше — лишняя нагрузка на сервер и конкуренция соединений за канал, меньше — страницы с десятками ресурсов грузятся медленно. Из этого же ограничения выросли старые приёмы вроде раздачи статики с нескольких поддоменов (domain sharding) и склейки файлов.
Для серверных интеграций аналог — размер пула. Если пул на 10 соединений, а одновременно нужно сделать 50 вызовов к партнёру, 40 будут ждать свободного соединения. Снаружи это выглядит как «партнёр медленный», хотя очередь у нас.
HTTP/2: мультиплексирование и новое место для HOL
HTTP/2 сохраняет семантику (методы, заголовки, коды), но меняет представление на проводе:
- Бинарные фреймы вместо текстовых строк. Сообщение режется на фреймы заголовков и данных.
- Потоки (streams): у каждой пары запрос-ответ свой идентификатор, фреймы разных потоков перемешиваются в одном TCP-соединении. Десятки запросов идут параллельно без очереди на уровне HTTP.
- HPACK — сжатие заголовков. Заголовки повторяются от запроса к запросу (cookie, авторизация, user-agent), поэтому стороны ведут общую таблицу уже переданных полей и ссылаются на неё по индексу, а новые значения кодируют кодом Хаффмана.
HTTP/2 почти всегда работает поверх TLS, версия выбирается через ALPN. Одного соединения на хост обычно достаточно.
Проблема HOL при этом не исчезла, а спустилась на уровень ниже. TCP гарантирует доставку байтов строго по порядку. Если потерялся один пакет, операционная система не отдаст приложению следующие байты, пока пакет не придёт повторно, — даже если они относятся к другим потокам, которым потеря не нужна. На сети с потерями (мобильная связь, перегруженный канал) все потоки встают разом. В таких условиях HTTP/2 на одном соединении может работать хуже, чем HTTP/1.1 с несколькими соединениями, где потеря тормозит только одно из них.
HTTP/3 и QUIC
HTTP/3 работает поверх QUIC — транспортного протокола поверх UDP. QUIC сам реализует то, что раньше делал TCP (надёжную доставку, контроль перегрузки), но с несколькими принципиальными отличиями:
- Независимые потоки на уровне транспорта. Потеря пакета задерживает только тот поток, чьи данные в нём были. Остальные продолжают получать байты. Это и есть решение HOL, которого не мог дать HTTP/2 поверх TCP.
- TLS 1.3 встроен в рукопожатие. Установка транспорта и согласование ключей совмещены, новое соединение стоит 1 RTT вместо 2 (TCP плюс TLS 1.3), а при возобновлении возможен 0-RTT с теми же оговорками про повтор.
- Миграция соединения. Соединение опознаётся по идентификатору (connection ID), а не по четвёрке адресов и портов. Телефон, перешедший с Wi-Fi на мобильную сеть, получает новый IP, но соединение продолжается без повторного рукопожатия.
- Сжатие заголовков — QPACK, вариант HPACK, адаптированный к тому, что потоки приходят вразнобой.
Ограничения тоже есть. Клиент обычно узнаёт о поддержке HTTP/3 из заголовка Alt-Svc или DNS-записи типа HTTPS, поэтому первое соединение часто идёт по TCP. Корпоративные сети нередко блокируют UDP на порту 443, и клиент откатывается на HTTP/2. На момент написания HTTP/3 заметно выигрывает в основном для браузеров и мобильных клиентов на нестабильных сетях. Для вызовов сервис-к-сервису внутри дата-центра, где потерь мало и соединения долгоживущие, выигрыш невелик.
Таймауты: кто что меряет
Фраза «таймаут 5 секунд» в требованиях почти всегда неполна. У HTTP-клиента несколько независимых таймаутов, и каждый срабатывает в своей фазе.
| Таймаут | Что меряет | Типичная причина срабатывания |
|---|---|---|
| Ожидание соединения из пула (pool acquire) | Сколько ждать свободное соединение | Пул мал для нагрузки, соединения заняты медленными вызовами |
| Connect timeout | Установку TCP-соединения | Хост недоступен, пакеты отбрасывает межсетевой экран, перегружен приём соединений |
| TLS handshake timeout | Рукопожатие TLS | Перегрузка сервера, проблемы с промежуточным узлом, инспекция TLS |
| Read (socket) timeout | Максимальную паузу между порциями полученных данных | Сервер принял запрос и долго обрабатывает, или ответ идёт с паузами |
| Write timeout | Паузу при отправке тела запроса | Сервер не читает тело, забит буфер |
| Idle timeout | Сколько неиспользуемое соединение живёт в пуле | Не ошибка, а политика закрытия |
| Общий дедлайн (call timeout, deadline) | Всё время вызова от начала до конца | Сумма всех фаз превысила бюджет |
Две детали, которые часто путают.
Первая: read timeout в большинстве библиотек — это не время на весь ответ, а максимальная пауза между чтениями из сокета. Сервер, который отдаёт по байту раз в 20 секунд при read timeout 30 секунд, никогда не вызовет срабатывания и продержит запрос сколько угодно. Поэтому нужен ещё и общий дедлайн.
Вторая: если партнёр принял TCP-соединение и завис на обработке, connect timeout уже неважен — соединение установлено за миллисекунды. Сработает read timeout или общий дедлайн. Если же партнёр упал целиком и его адрес не отвечает, сработает connect timeout.
Отдельная ловушка — несогласованные idle timeout. Балансировщик партнёра закрывает простаивающие соединения, скажем, через минуту, а наш пул считает их живыми дольше. Следующий запрос уходит в уже закрытое соединение и получает сброс. Ещё хуже, когда промежуточный NAT или межсетевой экран молча забывает простаивающее соединение: запрос уходит в пустоту, и клиент ждёт до read timeout. Правило: idle timeout клиента должен быть меньше, чем у сервера и всех узлов между ними.
И ещё одно: повтор по таймауту безопасен только для идемпотентных операций. Таймаут не означает, что запрос не выполнился, — возможно, потерялся только ответ. Как проектировать повторы для POST, разбирается в модуле 1.3.
Разбор примера: бюджет вызова платёжного шлюза
Интернет-магазин при оформлении заказа вызывает платёжный шлюз банка, расположенный в другой стране. Измеренный RTT — 50 мс, шлюз обрабатывает запрос в среднем за 150 мс, в 99-м перцентиле за 800 мс. Шлюз поддерживает только TLS 1.2. Требование бизнеса: пользователь должен получить результат не позже чем через 5 секунд.
Холодный вызов, имя уже в кэше резолвера: TCP 50 мс, TLS 1.2 ещё 100 мс, запрос с ответом 50 мс плюс обработка 150 мс. Итого около 350 мс. Тот же вызов по прогретому соединению из пула — около 200 мс. Разница в 150 мс на каждый холодный вызов — это аргумент за keep-alive и за то, чтобы idle timeout пула был согласован с настройками шлюза.
Теперь раскладываем 5 секунд на конкретные таймауты:
- connect timeout — порядка секунды: это в двадцать раз больше RTT, нормальное соединение устанавливается гораздо быстрее, а недоступный хост мы распознаем быстро и не сожжём весь бюджет на ожидании;
- TLS handshake timeout — тоже в пределах секунды-двух;
- read timeout — с запасом над 99-м перцентилем обработки, например 2–3 секунды;
- общий дедлайн — 4 секунды, чтобы магазину осталось время сформировать ответ пользователю;
- автоматический повтор — только если шлюз поддерживает ключ идемпотентности, и только в пределах оставшегося дедлайна.
Такой разбор и есть содержательная постановка: вместо одной цифры в требованиях появляются несколько, и у каждой есть обоснование.
Прокси и промежуточные узлы
Между клиентом и приложением почти всегда стоят посредники. Их полезно различать по тому, на чьей они стороне.
Прямой прокси (forward proxy) работает от имени клиента: корпоративный выход в интернет, узел с фиксированным IP для белых списков партнёра. Для HTTPS клиент отправляет прокси метод CONNECT host:443, прокси открывает TCP-туннель и дальше просто пересылает зашифрованные байты, не видя содержимого. Исключение — корпоративная инспекция TLS, когда прокси подменяет сертификаты своими (подробнее в модуле 1.2).
Обратный прокси (reverse proxy) работает от имени сервера: балансировщик нагрузки, API-шлюз, CDN, nginx перед приложением. Клиент думает, что разговаривает с сервером, а на самом деле — с посредником.
Балансировщики бывают двух уровней. L4-балансировщик пересылает TCP-соединения, не разбирая HTTP и не расшифровывая TLS. L7-балансировщик завершает TLS (TLS termination), читает HTTP, может маршрутизировать по пути и заголовкам, а к приложению идёт отдельным соединением — открытым или снова зашифрованным.
flowchart TD
Client["Клиент<br/>203.0.113.7"] -->|"HTTPS"| LB["L7-балансировщик<br/>терминирует TLS"]
LB -->|"HTTP или новый TLS<br/>X-Forwarded-For: 203.0.113.7"| GW["API-шлюз"]
GW -->|"X-Forwarded-For: 203.0.113.7, 10.0.1.5"| App["Сервис заказов"]
Отсюда ответ на вопрос, почему сервис видит IP балансировщика: TCP-соединение к сервису открыл балансировщик, и адрес источника на уровне сети — его. Всё, что сервис знает о настоящем клиенте, он знает из заголовков, которые добавили посредники:
X-Forwarded-For— фактический стандарт. Каждый прокси дописывает в конец адрес того, кто к нему подключился. Получается список: клиент, первый прокси, второй и так далее.X-Forwarded-ProtoиX-Forwarded-Host— исходная схема и имя хоста. Без них приложение за терминирующим балансировщиком думает, что запрос пришёл по HTTP, и генерирует неправильные ссылки и редиректы.Forwarded(RFC 7239) — стандартизованная замена с явными параметрами:
Forwarded: for=203.0.113.7;proto=https;by=10.0.1.5
- Для L4-балансировщиков, которые HTTP не трогают, есть протокол PROXY: адрес клиента передаётся служебным заголовком в начале TCP-соединения.
Слепо доверять этим заголовкам нельзя. Клиент может сам отправить X-Forwarded-For: 10.0.0.1, и наивный код, берущий первый элемент списка, решит, что запрос пришёл из внутренней сети. Корректный подход: знать список своих доверенных прокси, идти по списку справа налево, пропускать доверенные адреса и считать клиентом первый недоверенный. Соответственно, внешний балансировщик должен перезаписывать или очищать входящие заголовки, а не только дописывать. В nginx это делает модуль real_ip, в фреймворках — настройка доверенных прокси. Для антифрода, лимитов по IP и журналов аудита в банковской системе это требование к безопасности, а не деталь реализации.
Типичные ошибки
- Записывать в требования один «таймаут 5 секунд» без разделения на connect, read и общий дедлайн. Разработчики поставят его в одно место, и оставшиеся фазы окажутся без ограничения.
- Считать, что повтор по таймауту безопасен. Таймаут на чтении означает «ответ не пришёл», а не «операция не выполнилась».
- Менять IP сервиса без снижения TTL заранее и без периода, когда работают оба адреса, забывая про белые списки IP у партнёров.
- Разбирать задержку интеграции как проблему «медленного канала», хотя виноваты число RTT, отсутствие пула соединений или очередь на пуле внутри своего сервиса.
- Держать idle timeout пула дольше, чем у балансировщика партнёра, и получать периодические сбросы соединений на первом запросе после паузы.
- Брать IP клиента из первого элемента
X-Forwarded-Forбез списка доверенных прокси. - Ожидать, что HTTP/2 или HTTP/3 сами решат проблемы межсервисной интеграции, когда основное время уходит на обработку у партнёра.
Как говорить об этом на собеседовании
Уровень senior виден по умению разложить задержку на фазы и назвать цифры по порядку величины. Хорошая формулировка: «Холодный HTTPS-вызов по TLS 1.2 — это четыре RTT плюс обработка, по прогретому соединению — один RTT плюс обработка. Поэтому первым делом я смотрю, переиспользуются ли соединения и совпадают ли idle timeout с балансировщиком партнёра». Такая фраза показывает понимание механизма, а не знание терминов.
При разборе кейса вида «раз в несколько минут первый запрос медленный» перечисляйте гипотезы и способ проверки каждой: соединения закрыты по idle timeout и открываются заново (видно по TCP и TLS фазам в трассировке), истёк TTL и идёт повторный резолв (время DNS), промежуточный NAT забыл соединение, на стороне партнёра прогревается кэш или масштабируется инстанс. Хорошо добавить, как оформить результат в требованиях: конкретные таймауты с обоснованием, политика повторов, привязанная к идемпотентности, требования к передаче IP клиента через доверенные прокси.
Коротко
- Время API-вызова определяется числом RTT и обработкой на сервере, а не шириной канала. RTT ограничен физикой: около миллисекунды на каждые 100 км маршрута.
- DNS кэшируется на многих уровнях, включая пул соединений. Смена IP требует заранее сниженного TTL и периода параллельной работы.
- Новое HTTPS-соединение: TLS 1.2 — 4 RTT до ответа, TLS 1.3 — 3, HTTP/3 — 2. Переиспользованное — 1.
- 0-RTT экономит оборот, но допускает повтор запроса злоумышленником, поэтому применим только к безопасным и идемпотентным запросам.
- HTTP/1.1 — один запрос на соединение за раз, HTTP/2 мультиплексирует потоки, но страдает от HOL на уровне TCP, QUIC убирает HOL за счёт независимых потоков поверх UDP.
- Таймауты меряют разные фазы. Read timeout — пауза между данными, а не время на весь ответ, поэтому нужен ещё общий дедлайн.
- За L7-балансировщиком сервис видит IP балансировщика. Реальный адрес клиента передаётся заголовками, которым можно доверять только от своих прокси.
Читать дальше
- Ilya Grigorik, High Performance Browser Networking — бесплатно на hpbn.co. Главы: «Building Blocks of TCP», «Transport Layer Security», «HTTP/1.X», «HTTP/2». Книга 2013 года, но фундамент не устарел.
- RFC 9110 — HTTP Semantics — пролистать оглавление, чтобы знать, где что лежит; читать выборочно в модуле 1.3.
- Cloudflare Learning Center: «What is DNS», «What is HTTP/3».
- Julia Evans, Networking! ACK! и HTTP: Learn your browser’s language — быстрые и наглядные.
На system-design.space:
- Модель OSI
- Протокол TCP
- Система доменных имён (DNS)
- Протокол HTTP
- QUIC и HTTP/3: транспорт нового поколения
- Задержка (latency): физика, перцентили и бюджеты
- Визуализация: Динамика HTTP-запроса
Практика
- Разложить время одного запроса на фазы:
curl -o /dev/null -s -w "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n" https://example.comПовторить для сайта в соседней стране и на другом континенте. Посчитать RTT как
connect − dns. curl -v --http1.1иcurl -v --http2на один и тот же адрес — найти в выводе ALPN и договорённую версию протокола.- В DevTools → Network включить колонку Protocol и открыть вкладку Timing у запроса. Сопоставить фазы с пунктом 1.
nslookup/dig +traceна любой домен — пройти цепочку от корня до авторитетного сервера.
Вопросы для самопроверки
- Сколько RTT нужно до первого байта ответа для нового HTTPS-соединения по TLS 1.2, TLS 1.3 и HTTP/3? А для переиспользованного соединения?
- Чем connect timeout отличается от read timeout? Какой из них сработает, если партнёр принял соединение, но завис на обработке?
- Почему HTTP/2 не решил проблему head-of-line blocking полностью и как это решает QUIC?
- Интеграция стабильно отвечает за 40 мс, но раз в несколько минут первый запрос идёт 300 мс. Какие гипотезы?
- Что сервер знает о клиенте, если перед ним стоит L7-балансировщик? Как передать реальный IP клиента и почему этому заголовку нельзя слепо доверять?
- Почему 0-RTT в TLS 1.3 допустим для GET и опасен для POST?