Эволюция HTTP
HTTP (HyperText Transfer Protocol) -- основной протокол веба. За 30 лет он прошёл путь от простого текстового протокола до высокопроизводительного бинарного транспорта.
1991 1997 1999 2015 2022
│ │ │ │ │
HTTP/0.9 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/3
Только Заголовки Keep-alive Мультиплекс QUIC
GET статусы Chunked Бинарный 0-RTT
коды Pipelining HPACK QPACK
Host Server Push UDP
Зачем знать различия для System Design
- Выбор версии HTTP влияет на производительность API
- Понимание мультиплексирования важно при проектировании микросервисов
- HTTP/3 (QUIC) меняет поведение в нестабильных сетях (мобильные клиенты)
- На собеседовании: «Как бы вы оптимизировали взаимодействие между сервисами?»
HTTP/1.1
Ключевые особенности
Клиент Сервер
│ │
│── GET /index.html ──────────────►│
│◄── 200 OK + HTML ────────────────│
│ │
│── GET /style.css ───────────────►│ ← Последовательно!
│◄── 200 OK + CSS ─────────────────│
│ │
│── GET /script.js ───────────────►│ ← Ждёт предыдущий ответ
│◄── 200 OK + JS ──────────────────│
| Свойство | Описание |
|---|---|
| Keep-Alive | Повторное использование TCP-соединения |
| Pipelining | Отправка запросов без ожидания ответа (на практике отключено) |
| Chunked Transfer | Потоковая передача (без Content-Length) |
| Host заголовок | Виртуальные хосты на одном IP |
| Текстовый формат | Человекочитаемые заголовки |
Проблемы HTTP/1.1
Head-of-Line Blocking:
Соединение 1: GET /large-image.jpg ────────────────────► (2 сек)
GET /small-api.json ─── ЗАБЛОКИРОВАН ──► (ждёт image)
Обходное решение: 6 параллельных TCP-соединений к одному хосту
Соединение 1: GET /image1.jpg ──────────────────────►
Соединение 2: GET /image2.jpg ──────────────────────►
Соединение 3: GET /api/data ──────────────────────►
Соединение 4: GET /style.css ──────────────────────►
Соединение 5: GET /script.js ──────────────────────►
Соединение 6: GET /font.woff ──────────────────────►
Избыточность заголовков:
Каждый запрос несёт полные заголовки (~800 байт):
GET /api/v1/users HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64)...
Accept: application/json
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8
Accept-Encoding: gzip, deflate, br
Cookie: session=abc123; tracking=xyz789; preferences=...
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Connection: keep-alive
↑ Эти заголовки повторяются в КАЖДОМ запросе!
HTTP/2
Бинарное фреймирование
HTTP/2 использует бинарный формат вместо текстового. Каждое сообщение разбивается на фреймы.
HTTP/1.1 (текст): HTTP/2 (бинарный):
┌─────────────┐
GET /index.html HTTP/1.1 │ HEADERS │ Stream 1
Host: example.com → │ Frame │
Accept: text/html └─────────────┘
┌─────────────┐
│ DATA │ Stream 1
│ Frame │
└─────────────┘
Мультиплексирование
Главное преимущество HTTP/2 -- множество параллельных запросов в одном TCP-соединении.
HTTP/1.1: 6 TCP-соединений HTTP/2: 1 TCP-соединение
┌──TCP──┐ ┌──TCP──┐ ┌──TCP──┐ ┌────────────TCP─────────────┐
│ Req 1 │ │ Req 2 │ │ Req 3 │ │ Stream 1: HEADERS + DATA │
│ Res 1 │ │ Res 2 │ │ Res 3 │ │ Stream 3: HEADERS + DATA │
└───────┘ └───────┘ └───────┘ │ Stream 5: HEADERS + DATA │
┌──TCP──┐ ┌──TCP──┐ ┌──TCP──┐ │ Stream 7: HEADERS + DATA │
│ Req 4 │ │ Req 5 │ │ Req 6 │ │ Stream 9: HEADERS + DATA │
│ Res 4 │ │ Res 5 │ │ Res 6 │ │ Stream 11: HEADERS + DATA │
└───────┘ └───────┘ └───────┘ └────────────────────────────┘
6 TCP handshakes + 6 TLS 1 TCP handshake + 1 TLS
Фреймы могут чередоваться (interleaving):
Одно TCP-соединение:
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ H(1) │ H(3) │ D(1) │ D(3) │ D(1) │ H(5) │ D(5) │
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘
│ │ │ │ │ │ │
│ │ │ │ │ │ └── Data Stream 5
│ │ │ │ │ └── Headers Stream 5
│ │ │ │ └── Data Stream 1 (continued)
│ │ │ └── Data Stream 3
│ │ └── Data Stream 1
│ └── Headers Stream 3
└── Headers Stream 1
HPACK: Сжатие заголовков
HTTP/2 использует HPACK для сжатия заголовков, устраняя избыточность.
Первый запрос: все заголовки передаются полностью
┌─────────────────────────────────────────────┐
│ :method: GET │
│ :path: /api/v1/users │
│ :authority: api.example.com │
│ authorization: Bearer eyJhbGci... │
│ accept: application/json │
└─────────────────────────────────────────────┘
→ Заголовки добавляются в динамическую таблицу
Второй запрос: только различия
┌─────────────────────────────────────────────┐
│ :path: /api/v1/orders ← Изменилось │
│ (остальные = ссылки на индексы в таблице) │
└─────────────────────────────────────────────┘
→ Экономия ~90% размера заголовков
Статическая таблица HPACK содержит 61 самый частый заголовок:
| Индекс | Заголовок | Значение |
|---|---|---|
| 1 | :authority | |
| 2 | :method | GET |
| 3 | :method | POST |
| 4 | :path | / |
| 5 | :path | /index.html |
| 8 | :status | 200 |
| ... | ... | ... |
Server Push
Сервер может отправить ресурсы ДО того, как клиент их запросит.
Клиент Сервер
│ │
│── GET /index.html ──────────────►│
│ │ Сервер знает, что для index.html
│◄── PUSH_PROMISE /style.css ──────│ нужны style.css и script.js
│◄── PUSH_PROMISE /script.js ──────│
│◄── 200 OK /index.html ───────────│
│◄── 200 OK /style.css (pushed) ───│ ← Клиент не запрашивал!
│◄── 200 OK /script.js (pushed) ───│ ← Клиент не запрашивал!
На практике: Server Push в HTTP/2 оказался сложным в использовании и был удалён из Chrome в 2022 году. Лучшая альтернатива -- 103 Early Hints.
Stream Priority
HTTP/2 позволяет задавать приоритеты потокам:
Stream 1 (HTML): вес 256 ← Максимальный приоритет
Stream 3 (CSS): вес 220 ← Высокий (блокирует рендер)
Stream 5 (JS): вес 183 ← Средний
Stream 7 (Image): вес 110 ← Низкий
Stream 9 (Tracker): вес 16 ← Минимальный
Оставшаяся проблема HTTP/2
Мультиплексирование решает HTTP-level HOL blocking, но TCP-level HOL blocking остаётся:
TCP-поток: [Пакет 1][Пакет 2][Пакет X][Пакет 4][Пакет 5]
↑
Потерян пакет 3
Все потоки HTTP/2 ЗАБЛОКИРОВАНЫ, пока TCP не ретрансмитит пакет 3
Даже если пакет 4 и 5 относятся к другим HTTP-потокам!
HTTP/3
QUIC как транспорт
HTTP/3 работает поверх QUIC (UDP) вместо TCP, полностью устраняя TCP-level HOL blocking.
HTTP/2 over TCP: HTTP/3 over QUIC:
┌───────────┐ ┌───────────┐
│ HTTP/2 │ │ HTTP/3 │
├───────────┤ ├───────────┤
│ TLS 1.3 │ │ QUIC │ ← TLS встроен
├───────────┤ │ (streams) │ ← Независимые потоки
│ TCP │ ├───────────┤
├───────────┤ │ UDP │
│ IP │ ├───────────┤
└───────────┘ │ IP │
└───────────┘
QPACK: Сжатие заголовков для HTTP/3
QPACK -- адаптация HPACK для QUIC с учётом возможной переупорядоченности пакетов.
HPACK (HTTP/2): QPACK (HTTP/3):
Требует строгий порядок Допускает переупорядоченность
Encoder ──── TCP ────► Decoder Encoder ── QUIC Stream ──► Decoder
(порядок гарантирован TCP) (каждый поток независим)
Два специальных потока:
- Encoder Stream (обновления таблицы)
- Decoder Stream (подтверждения)
Установление соединения: сравнение
HTTP/1.1 + TLS 1.2: 3 RTT
TCP Handshake: 1 RTT ─────────────────
TLS 1.2: 2 RTT ─────────────────
→ Данные
HTTP/2 + TLS 1.3: 2 RTT
TCP Handshake: 1 RTT ─────────────────
TLS 1.3: 1 RTT ─────────────────
→ Данные
HTTP/3 (QUIC) первое: 1 RTT
QUIC + TLS 1.3: 1 RTT ─────────────────
→ Данные
HTTP/3 (QUIC) повторное: 0 RTT
0-RTT: 0 RTT → Данные сразу!
Connection Migration
Сценарий: пользователь переходит с Wi-Fi на мобильную сеть
HTTP/2 (TCP):
Wi-Fi: IP 192.168.1.5 → TCP connection
Переход: Соединение разорвано ✗
Мобильная: IP 10.0.0.1 → Новое TCP-соединение + TLS
(2 RTT потеряно)
HTTP/3 (QUIC):
Wi-Fi: IP 192.168.1.5 → QUIC Connection ID: 0x1234
Переход: Connection ID сохраняется ✓
Мобильная: IP 10.0.0.1 → Тот же Connection ID: 0x1234
(0 RTT, бесшовный переход)
Сравнительная таблица
| Характеристика | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Год | 1997 | 2015 | 2022 |
| Транспорт | TCP | TCP | QUIC (UDP) |
| Формат | Текстовый | Бинарный | Бинарный |
| Мультиплексирование | Нет (6 соединений) | Да (1 соединение) | Да (1 соединение) |
| HOL Blocking | HTTP + TCP | Только TCP | Нет |
| Сжатие заголовков | Нет | HPACK | QPACK |
| Server Push | Нет | Да (deprecated) | Нет |
| Шифрование | Опционально | Де-факто (TLS) | Обязательно (TLS 1.3) |
| 0-RTT | Нет | Нет | Да (повторное) |
| Connection Migration | Нет | Нет | Да |
| Adoption (2025) | ~30% | ~40% | ~30% |
Практический выбор в System Design
Когда какую версию использовать
Внутренние микросервисы (дата-центр)?
├── gRPC (HTTP/2) — стриминг, schema, производительность
└── HTTP/1.1 — простота, совместимость с legacy
Публичное API для веб-клиентов?
├── HTTP/2 — стандарт, широкая поддержка
└── HTTP/3 — мобильные клиенты, нестабильная сеть
Мобильное приложение?
├── HTTP/3 — connection migration, 0-RTT
└── HTTP/2 — если HTTP/3 не поддерживается
CDN / статика?
└── HTTP/3 + HTTP/2 fallback — максимальная скорость
Оптимизации на уровне HTTP
| Техника | HTTP/1.1 | HTTP/2+ |
|---|---|---|
| Domain sharding | Да (обход лимита 6 conn) | Нет (вредит!) |
| Sprite sheets | Да (уменьшение запросов) | Не нужно |
| Inline CSS/JS | Да (уменьшение запросов) | Не нужно |
| Bundle JS | Да (1 большой файл) | Мелкие модули лучше |
| Resource hints | Да (prefetch/preconnect) | Да + 103 Early Hints |