Все углублённые блоки
Углублённые блоки
Блок 02 / Углублённый разбор

TLS, HTTP-потоки, пулы и посредники

Проверяем доверие к каналу, сравниваем HTTP/2 и HTTP/3 и проектируем connection pools, балансировку и кэширование.

Клуб добавил балансировщик и HTTP/2. Сертификат в браузере действителен, но до приложения запрос идёт по HTTP. Один backend получает почти весь поток, а после переключения DNS часть обращений всё ещё идёт на прежний адрес. Ни одна из этих ситуаций не противоречит наличию HTTPS и современного протокола.

Здесь разберём отдельные решения: где заканчивается защита TLS, какой поток блокирует потеря, что ограничивает пул и на каком уровне распределяется работа. До начала нужны DNS, RTT, TCP и семантика HTTP.

Доверие проверяется до полезного обмена

При обычном HTTPS клиент проверяет цепочку сертификатов до доверенного корня и допустимость сертификата, включая срок и назначение. Он также сопоставляет имя сервиса с именем в сертификате. Доверенный issuer не делает сертификат для другого сайта подходящим. Обмен ключами и защита сообщений определены в TLS 1.3, RFC 8446; правила проверки идентичности — в RFC 9525.

SNI передаёт имя для выбора серверной конфигурации. ALPN позволяет согласовать прикладной протокол, например HTTP/2. Они решают разные задачи и не заменяют проверку сертификата. Защищённый канал также не устанавливает личность пользователя приложения: эту проверку выполняют сессия и правила доступа.

Если proxy завершает клиентский TLS, он получает расшифрованный HTTP. Дальше proxy может установить отдельный TLS к приложению или передать данные незашифрованно. На схеме необходимо подписать каждый участок и кто проверяет другую сторону. Для взаимной аутентификации сервисов может использоваться mTLS; наличие клиентского сертификата всё равно не определяет разрешение на любое действие.

TLS resumption сокращает повторную установку защищённого обмена. Ранние данные 0-RTT в TLS 1.3 имеют риск повторного воспроизведения: приложение должно учитывать replay. Нельзя автоматически передавать так необратимый POST только ради экономии задержки. Точное число RTT зависит от нового или восстановленного соединения, версии транспорта и поведения клиента.

Проверка границ

Нарисуйте путь browser → edge → API → database. На browser→edge есть TLS, на edge→API — обычный HTTP. Что защищает сертификат в браузере? Какое изменение защищает второй участок?

Разбор. Сертификат относится к серверной стороне первого TLS-канала. Он не доказывает шифрование до API или базы. Для второго участка нужен отдельный защищённый канал с проверкой ожидаемой стороны; TLS passthrough — другой вариант, при котором edge не разбирает HTTP внутри этого канала. Выбор зависит от нужных функций маршрутизации и модели доверия.

HTTP/2 и QUIC: где сохраняется ожидание

HTTP/1.1 может переиспользовать TCP-соединение, но обычный клиент без pipelining выполняет на нём один запрос за другим. Несколько соединений дают параллелизм ценой памяти и установления связи. HTTP/2 передаёт frames разных streams по одному TCP-потоку и позволяет запросам выполняться параллельно. Лимит streams и окна flow control ограничивают этот параллелизм. Источник: RFC 9113.

Пусть TCP-сегмент с данными потока A потерян, а следующий сегмент содержит данные B. TCP не передаст последующие байты приложению до восстановления дырки. HTTP/2 не может увидеть готовый frame B: блокировка возникает на транспортном уровне, хотя HTTP-потоки логически независимы.

QUIC использует UDP для передачи пакетов, но сам реализует надёжность, защищённый обмен, congestion control и потоки. Для каждого потока данные упорядочиваются отдельно. Потеря пакета с данными A не требует задерживать полученные данные B, если B не зависит от потерянных данных. Пакет способен содержать frames нескольких потоков, поэтому его потеря затрагивает каждый из них. Это свойство определено в RFC 9000, §13.

HTTP/3 поверх QUIC не устраняет общий congestion control, лимиты ресурсов и зависимость приложения от одного медленного запроса. Сжатие заголовков QPACK тоже может создавать ожидание нужных данных: RFC 9114, §4.2.1. Блокировка UDP на маршруте может потребовать другой транспорт. Прежде чем выбрать HTTP/3, сравните поддержку клиентов/посредников, профиль потерь и задержек, CPU и поведение fallback.

Самостоятельно. В одном пакете QUIC были данные A и C; он потерялся, данные B получены в другом пакете. Какие потоки могут продолжать работу? Почему это не обещание завершить B раньше A?

Разбор. B может читать доступные байты независимо от восстановления A/C на транспорте. Но бизнес-логика B может ждать результат A, а общая сеть и лимиты могут задержать новые данные. Ответ «потеря никогда не влияет на другие потоки QUIC» слишком сильный.

Пул ограничивает ресурс, а deadline — ожидание

Пул HTTP-соединений удерживает открытые соединения к upstream и передаёт запрос, когда доступна ёмкость. Для HTTP/1.1 это часто свободное соединение; для HTTP/2/3 — свободный stream в соединении. Поэтому максимум 10 connections не означает максимум 10 запросов. Конкретное поведение зависит от библиотеки; пример реализации дан в Envoy connection pooling.

Подпишите две стороны proxy: downstream — входящие клиентские соединения, upstream — исходящие к приложению. Тысяча клиентов не требует тысячи upstream connections, но может создать тысячу активных дорогих запросов. Лимиты соединений, активных запросов и ожидающих запросов не взаимозаменяемы. При масштабировании процессы могут каждый открыть собственный пул: локальный лимит умножается на число процессов.

Таймауты отвечают на разные вопросы: сколько ждать место в пуле; сколько устанавливать TCP/TLS; сколько ждать ответ; сколько разрешать простоя чтения; сколько жить всей операции. Idle timeout срабатывает при отсутствии ожидаемой активности и не является общим сроком активного потока. У каждого клиента нужно проверить, покрывает ли общий timeout DNS, pool acquisition и handshake. Пример различий в реальном proxy: Envoy timeouts.

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

Расчёт бюджета

Клиентский срок 800 мс. Очередь заняла 300, connect+TLS — 150, зависимость — 250, возврат ответа — 50. Остаётся 50 мс. Если следующая попытка обычно требует 200 мс, повтор не укладывается. При каждом участке с отдельным timeout 800 мс общий срок перестаёт быть ограничен 800. Выигрыш от reuse не даёт права перестать измерять очередь пула.

Балансировка соединений и запросов

Forward proxy действует со стороны клиента, reverse proxy — со стороны сервиса. L4-балансировка обычно выбирает backend на уровне транспортного соединения; L7 видит прикладные сообщения и может выбрать маршрут HTTP-запроса по host/path/другим правилам. Термины указывают уровень решения, но не определяют автоматически TLS passthrough, NAT или устройство всей сети. HTTP-посредники описаны в RFC 9110.

Два HTTP/2-соединения создают 900 и 100 запросов/с. Round robin по соединениям направляет по одному соединению на каждый backend, но получает поток 900/100. L7 может распределять новые запросы иначе, если архитектура допускает это; после открытия долгого stream его нельзя без прикладного восстановления просто перенести. Даже 500/500 запросов не доказывает равную стоимость, если на одной стороне тяжёлые отчёты.

При остановке backend сначала прекращают направлять новую работу, затем завершают активную до ограниченного срока. Проверка готовности и drain решают разные вопросы. Долгие SSE/WebSocket-потоки требуют явного reconnect с восстановлением из истории: ожидать их естественного завершения бесконечно нельзя.

CDN: какие ответы можно считать одинаковыми

CDN — сеть посредников, способных доставлять и кэшировать данные ближе к клиенту. При hit origin не обслуживает этот запрос; при miss требуется получение исходного объекта. Для одинаковых публичных сегментов это полезно. Для персонального ответа сначала нужно решить, кто вправе получить байты и какие запросы взаимозаменяемы.

Cache key выбирает объект кэша; Vary сообщает, какие headers влияют на представление. private ограничивает использование shared cache; no-store запрещает хранить; no-cache требует проверки перед повторным использованием, а не обязательно запрещает сохранение. Validator, например ETag, позволяет проверить, изменилось ли представление. Эти контракты описаны в RFC 9111.

Если язык ответа зависит от Accept-Language, а кэш не учитывает его, другой клиент может получить неправильный язык. Если разрешение проверяется только на origin при miss, hit способен обойти нужную проверку доступа. Для закрытого видео проверяйте авторизацию перед каждой защищённой выдачей, включая hit, согласно контракту конкретного CDN.

Локальная практика и её границы

Прочитайте метаданные своего локального HTTP-сервера, когда он запущен, или публичного учебного адреса:

curl -sS -D - -o /dev/null --max-time 10 https://example.com/

Найдите status, Cache-Control, ETag, Vary, Age, если они присутствуют. Отсутствие header не является неисправностью само по себе. Для своего endpoint с ETag повторите GET с If-None-Match, подставив полученное значение; ожидайте 304 только если сервер поддерживает условный запрос и представление не изменилось. Такая проба проверяет HTTP contract, а не попадание в каждый узел глобального CDN.

Задание: оценить улучшение по границам

Было 100 запросов: 90 ответов по 10 КБ из CDN и 10 ответов по 10 МБ через origin. Посчитайте request hit ratio и byte hit ratio. Затем объясните, почему срок ссылки 60 секунд не гарантирует отзыв доступа через 60 секунд во всех вариантах выдачи.

Подсказки. Сначала сложите полезные байты; отдельно назовите момент проверки разрешения и уже переданные данные.

Разбор. Request hit ratio — 90%. Полезные байты hits — 0,9 МБ; всех ответов — 100,9 МБ в десятичных единицах. Byte hit ratio около 0,89%; origin экономит намного меньше доли байтов, чем доли запросов. Пользовательский egress может оставаться оплачиваемым даже для hits. Для ссылки надо проверить контракт CDN: когда оценивается срок, как обслуживается начатый ответ, что происходит при новых Range/segment запросах. Уже полученные клиентом байты истечение срока не забирает.

Успешное решение отмечает TLS-границы, тип балансировки, лимиты pools/streams, общий deadline и cache key. Каждое утверждение о выигрыше должно иметь измеряемую границу и условие, при котором оно перестаёт работать.

Проверьте себя

Одно HTTP/2-соединение приносит 900 запросов/с, другое — 100. L4 round robin направил по одному соединению на каждый из двух backend. Почему одинаковое число connections не доказывает равную нагрузку?

Запишите ход рассуждений, расчёты и вопросы. Сохраните текст перед уходом со страницы. После входа в аккаунт ответ участвует в общей синхронизации прогресса. Автоматической оценки архитектуры здесь нет.