Real-Time Backend

HTTP и его ограничения

От документов к приложениям: 30 лет эволюции протокола

1991 год: Тим Бернерс-Ли создаёт HTTP/0.9 в CERN - протокол для загрузки HTML-документов. Один запрос, один документ. 2005-2009: Web 2.0. Gmail, Google Maps, Facebook. Разработчики придумывают Comet, Long Polling, Bayeux protocol - всё это костыли поверх HTTP. 2009: Райан Даль создаёт Node.js, показывая что event-loop эффективнее thread-per-request для I/O-intensive задач. 2011: RFC 6455 стандартизирует WebSocket - протокол, изначально созданный для full-duplex коммуникации. Ограничения HTTP не исчезли - они стали мотивацией для нового протокола.

Slack в 2013 году тратил сотни тысяч долларов на инфраструктуру polling, обслуживая 15,000 запросов в секунду - из которых 99.9% возвращали пустой ответ. Каждый из этих запросов тащил 600 байт заголовков. После перехода на WebSocket нагрузка упала в 100 раз при той же функциональности.

  • **Facebook чат (2008)** - изобрёл Long Polling для миллионов пользователей, пока WebSocket не был стандартом
  • **Slack** - работал на polling до 2015 года, потом перешёл на WebSocket: нагрузка упала в 100 раз
  • **Биржи (Binance, Coinbase)** - WebSocket API для live orderbook: HTTP polling на таких объёмах физически невозможен
  • **Google Docs** - Operational Transformation через Long Polling до 2013 года, потом WebSocket
  • **GitHub Actions** - live logs через SSE поверх HTTP/2: Server-Sent Events без WebSocket overhead
  • **Notion** - real-time collaboration через WebSocket: каждое нажатие клавиши синхронизируется за <50 мс

Предварительные знания

  • Что такое Real-Time

Request-Response: однонаправленная модель

1991 год. Тим Бернерс-Ли в CERN создаёт HTTP для обмена научными статьями. Одна страница - один запрос - один ответ. Идеально для физиков-ядерщиков. Через 30 лет на этом же протоколе работают WhatsApp, Slack и торговые терминалы. Фундаментальное ограничение никуда не делось: **сервер физически не может отправить данные без предварительного запроса от клиента**.

HTTP строго построен на принципе **клиент инициирует - сервер отвечает**. Это half-duplex: данные идут в одном направлении за раз. Сервер не хранит информацию о клиенте между запросами (stateless). Каждый запрос - с нуля: заголовки, cookies, авторизация - заново.

ХарактеристикаHTTP Request-ResponseReal-time идеал
ИнициаторТолько клиентОбе стороны
НаправлениеHalf-duplexFull-duplex
СостояниеStateless (каждый запрос с нуля)Persistent connection
ЛатентностьЗависит от частоты запросовМгновенная доставка
Overhead500-800 байт заголовков при каждом запросеМинимальный после handshake

Каждый HTTP-запрос несёт 500-800 байт заголовков, даже если полезные данные - пустая строка. Cookie на крупном сайте добавляет ещё 200-500 байт. При polling каждые 2 секунды 10,000 пользователей генерируют ~5,500 байт/с только на метаданные протокола.

HTTP/2 и HTTP/3 решают проблему real-time через Server Push

Server Push в HTTP/2 предназначен для предзагрузки ресурсов (CSS, JS, images), а не для push-уведомлений. Модель остаётся request-response. Фактически браузеры убирают поддержку HTTP/2 Push как неиспользуемую функцию.

HTTP/2 Server Push отправляет ресурс проактивно в контексте уже открытого запроса - оптимизация загрузки страницы. Механизм push-уведомлений требует persistent connection без привязки к конкретному запросу - это WebSocket или SSE.

Почему HTTP-сервер не может отправить push-уведомление клиенту о новом сообщении?

Short Polling: наивный подход

Раз сервер не может уведомить клиента - что если клиент будет спрашивать постоянно? Short Polling: запрос каждые N секунд. Принципы LMAX: каждый тик торгового движка - polling на новые ордера. Slack в 2013 году до WebSocket: браузеры опрашивали серверы каждые 500 мс. Просто, работает везде. Цена считается ниже.

Посчитаем нагрузку. Чат-приложение: **10,000 пользователей онлайн**, polling каждые **2 секунды**, ~50 сообщений в минуту в группе.

Интервал pollingReq/s (10k пользователей)Средняя латентностьТрафик впустую/час
500 мс20,000250 мс68.4 GB
1 с10,000500 мс34.2 GB
2 с5,0001 с17.1 GB
5 с2,0002.5 с6.8 GB
30 с33315 с1.1 GB

Дилемма Short Polling: уменьшить интервал - растёт нагрузка и трафик. Увеличить - растёт латентность. Для dashboard с обновлением раз в 30 секунд Short Polling - нормальный выбор. Для live trading или чата - катастрофа.

Short Polling всегда плохо, его нигде не используют

Short Polling - хорошее решение для редко обновляемых данных: статус заказа, курсы валют раз в минуту, мониторинг раз в 30 секунд. Там где простота важнее мгновенной доставки.

Не каждое приложение требует миллисекундную задержку. Dashboard с обновлением раз в минуту не нуждается в WebSocket. Short Polling прост, надёжен, работает за любыми прокси и файрволами, не требует сложной серверной инфраструктуры.

10,000 пользователей с polling-интервалом 2 секунды. Сколько запросов в секунду получит сервер?

Long Polling: хитрый трюк

2008 год. Facebook чат обслуживает десятки миллионов пользователей. Short Polling - слишком дорого. WebSocket - ещё не стандарт. Инженеры Facebook изобретают **Long Polling**: вместо мгновенного ответа 'ничего нового' - сервер молчит, держит соединение открытым и отвечает только когда появятся данные. Задержка доставки стремится к нулю.

**Comet** - так в 2006 году назвали набор техник server push поверх HTTP. Long Polling - самая популярная из них. Название игра слов: Ajax и Comet - оба бренды чистящих средств. Twitter использовал Long Polling до 2013 года. Gravatar, Basecamp - тоже. Сейчас это легаси-паттерн: WebSocket или SSE делают то же самое без overhead HTTP handshake на каждое переподключение.

СвойствоShort PollingLong Polling
Задержка доставки0 до N секунд (среднее N/2)~0 мс (как только данные готовы)
Запросов в секундуN пользователей / интервал~0 в idle, spike при событии
Открытых соединенийКороткие, закрываютсяДо 1 на пользователя постоянно
Сложность сервераМинимальнаяНужно хранить waitingClients map
СовместимостьВезде, любой проксиВезде, но прокси может обрывать длинные запросы

В чём ключевое отличие Long Polling от Short Polling?

Цена HTTP: заголовки, handshake, bandwidth

Short Polling расточителен, Long Polling - сложен. Но у обоих общая боль: **каждый HTTP-запрос тащит сотни байт заголовков**, и каждое новое TCP-соединение требует рукопожатия. Биржевой движок Binance обрабатывает 100,000 ордеров/с. Если бы каждый ордер стоил HTTP handshake - 2.5-3.5 RTT по 50 мс - система работала бы с задержкой в секунды.

КомпонентOverheadПримечание
HTTP Request Headers600-800 байтПри каждом запросе
HTTP Response Headers300-500 байтПри каждом ответе
TCP Handshake1.5 RTTНовое соединение
TLS Handshake1-2 RTTHTTPS (обязателен в продакшне)
TCP Slow Start~10 пакетов разгонОграничивает пропускную способность вначале
Cookie (типичный)100-500 байтКаждый запрос к домену

HTTP/2 решает часть проблем: мультиплексирование нескольких запросов по одному TCP, сжатие заголовков HPACK. Но фундаментальная модель request-response остаётся. Сервер по-прежнему не может инициировать отправку данных без запроса клиента - это решает только WebSocket или SSE.

HTTP/2 мультиплексирование полностью решает проблему real-time

HTTP/2 оптимизирует передачу данных: сжатие заголовков HPACK, мультиплексирование запросов по одному TCP. Но модель не меняется - сервер не может инициировать передачу.

Мультиплексирование позволяет параллельно отправлять несколько запросов по одному TCP-соединению - это ускоряет загрузку страниц. Но каждый запрос всё равно инициирует клиент. Для настоящего real-time нужен WebSocket или SSE.

При Short Polling чата (10k пользователей, интервал 2с, ~50 сообщений/мин), какой процент трафика составляют полезные данные?

Ключевые выводы

  • HTTP - request-response протокол 1991 года: сервер не может инициировать отправку данных клиенту
  • Short Polling (запрос каждые N секунд): 99.998% трафика - пустые заголовки при 10k пользователях
  • Long Polling: сервер держит соединение до данных - нулевая задержка, но каждое переподключение = HTTP overhead
  • Каждый HTTP-запрос несёт 500-800 байт заголовков; TCP+TLS handshake = 2.5-3.5 RTT на новое соединение
  • HTTP/2 HPACK сжимает заголовки и мультиплексирует запросы - но request-response модель не меняется
  • Short Polling - нормально для раз-в-минуту обновлений; WebSocket/SSE - для настоящего real-time

Связь с другими темами

HTTP-ограничения - мотивация для появления WebSocket, SSE и других real-time протоколов:

  • WebSocket — Решает все проблемы HTTP для real-time: full-duplex, минимальный overhead, persistent connection
  • Server-Sent Events (SSE) — Компромисс: server push поверх HTTP без WebSocket upgrade
  • HTTP Fundamentals — Подробная механика HTTP/1.1, HTTP/2 и HPACK

Вопросы для размышления

  • При каком сценарии Short Polling - лучший выбор, несмотря на неэффективность?
  • Почему HTTP создали как stateless request-response? Какие преимущества это даёт для веб-страниц?
  • Как бы выглядело API чата на базе Long Polling при 1 миллионе одновременных пользователей?

Связанные уроки

  • rt-01-what-is-realtime — Определение real-time latency из предыдущего урока
  • rt-03-sse — SSE - следующий шаг: server push поверх HTTP
  • bt-05-http-fundamentals — Детали HTTP/1.1 и HTTP/2 механики
  • bt-08-websocket — WebSocket решает все ограничения, разобранные здесь
  • bt-07-http2-http3 — HTTP/2 мультиплексирование и его реальные пределы
  • isd-18-case-chat-system
  • net-21-http-basics
  • net-24-http2-http3
HTTP и его ограничения

0

1

Войти