Сетевые протоколы и коммуникация
От битов до API: как данные путешествуют по сети
Любая распределённая система — это сеть взаимодействующих компонентов. Серверы обмениваются данными, клиенты делают запросы, сервисы вызывают друг друга. Всё это происходит по сети, и выбор протоколов коммуникации критически влияет на производительность, надёжность и масштабируемость системы.
В этой главе мы разберём сетевой стек снизу вверх: от транспортного уровня (TCP/UDP) до прикладных протоколов (HTTP, WebSocket, gRPC). Вы поймёте не только «что», но и «почему» — когда использовать каждый инструмент и какие trade-offs при этом возникают.
0 Что такое сетевой протокол
Прежде чем погружаться в конкретные протоколы, ответим на базовый вопрос: что это вообще такое и зачем они нужны.
Определение
Сетевой протокол — это набор правил и соглашений, которые определяют, как именно два узла сети обмениваются данными. Протокол описывает:
- Формат сообщений — как именно выглядят передаваемые данные (структура пакета, заголовки, кодировка)
- Порядок обмена — кто говорит первым, как подтверждается получение, как завершается сеанс
- Поведение при ошибках — что делать, если пакет потерян, пришёл повреждённым или не пришёл вовсе
- Управление соединением — как оно устанавливается и закрывается
Без протоколов два компьютера, даже физически соединённые кабелем, не смогут «договориться»: один будет посылать данные в одном формате, другой — ожидать совершенно другого.
Хорошая аналогия: протокол — это язык и этикет одновременно. Язык определяет слова и грамматику (формат данных), этикет — правила разговора (кто говорит, как перебить, как попрощаться).
Модель OSI и стек TCP/IP
Сетевое взаимодействие устроено слоями: каждый слой решает свою задачу и предоставляет сервис слою выше, не зная деталей реализации слоёв ниже. Это называется инкапсуляцией.
Теоретическая модель OSI описывает 7 уровней, но на практике повсеместно используется упрощённая модель TCP/IP из 4 уровней:
┌─────────────────────────────────────────────┐
│ Прикладной уровень │ HTTP, gRPC, WebSocket, DNS, SMTP
│ (Application Layer) │ «Что передаём и в каком формате»
├─────────────────────────────────────────────┤
│ Транспортный уровень │ TCP, UDP
│ (Transport Layer) │ «Как надёжно доставить данные»
├─────────────────────────────────────────────┤
│ Сетевой уровень │ IP (IPv4, IPv6)
│ (Internet Layer) │ «Как найти путь между узлами»
├─────────────────────────────────────────────┤
│ Уровень доступа к сети │ Ethernet, Wi-Fi, оптика
│ (Network Access Layer) │ «Как передать биты по физической └─────────────────────────────────────────────┘ среде»
Когда браузер делает запрос к серверу, происходит следующее:
Браузер формирует HTTP-запрос
↓ (прикладной уровень)
TCP оборачивает его в сегмент, добавляет порты и управляющие флаги
↓ (транспортный уровень)
IP оборачивает сегмент в пакет, добавляет IP-адреса отправителя и получателя
↓ (сетевой уровень)
Ethernet оборачивает пакет во фрейм и отправляет по кабелю/Wi-Fi
↓ (уровень доступа к сети)
На принимающей стороне всё разворачивается в обратном порядке. Каждый слой «снимает» свою обёртку и передаёт содержимое наверх. Это и есть инкапсуляция — каждый уровень не знает и не должен знать, что находится внутри пакета уровня выше.
Зачем это знать разработчику
В большинстве случаев разработчики работают на прикладном уровне — пишут REST API, делают HTTP-запросы, используют WebSocket. Нижележащие уровни скрыты за абстракциями. Однако понимание всего стека критически важно, когда:
- вы отлаживаете загадочные задержки и не понимаете, где теряется время
- вы выбираете между TCP и UDP для нестандартного протокола
- вы объясняете, почему HTTP/3 быстрее HTTP/2 на мобильных сетях
- вы проектируете систему с требованием latency < 1 мс
На System Design интервью знание стека отличает кандидата, который «использовал HTTP», от того, кто понимает, что за ним стоит.
1 TCP и UDP: фундамент сетевой коммуникации
Прежде чем говорить о высокоуровневых протоколах, нужно понять транспортный уровень. TCP и UDP — два основных транспортных протокола интернета, каждый со своими характеристиками и областями применения.
TCP (Transmission Control Protocol)
TCP — протокол с установлением соединения, гарантирующий надёжную доставку данных в правильном порядке.
Трёхстороннее рукопожатие (Three-Way Handshake)
Перед передачей данных TCP устанавливает соединение:
Клиент → Сервер: SYN (seq=x)
Сервер → Клиент: SYN-ACK (seq=y, ack=x+1)
Клиент → Сервер: ACK (ack=y+1)
--- Соединение установлено, можно передавать данные ---
Это добавляет 1.5 RTT накладных расходов при каждом новом соединении — важная деталь для проектирования высоконагруженных систем.
📌 RTT (Round Trip Time) — время, за которое пакет уходит от отправителя к получателю и обратно. Типичные значения: в пределах датацентра 0.1–1 мс, межрегионально 10–100 мс, межконтинентально 100–300 мс.
Ключевые характеристики TCP:
- Надёжность: гарантирует доставку всех пакетов
- Упорядоченность: данные приходят в том порядке, в котором отправлены
- Контроль потока: отправитель не «затопит» медленного получателя
- Контроль перегрузки: алгоритмы slow start, congestion avoidance
- Full-duplex: одновременная передача данных в обоих направлениях
UDP (User Datagram Protocol)
UDP — «ненадёжный» протокол без установления соединения. Он не гарантирует доставку, порядок пакетов и не контролирует перегрузку. Звучит плохо? На самом деле это его главное преимущество.
Ключевые характеристики UDP:
- Нет установления соединения — нет накладных расходов на handshake
- Нет подтверждений — минимальная задержка
- Нет гарантий доставки — приложение само решает, как обрабатывать потери
- Broadcast и Multicast — можно отправить пакет нескольким получателям одновременно
- Меньший заголовок: 8 байт против 20+ байт у TCP
Сравнение TCP и UDP
| Характеристика | TCP | UDP |
|---|---|---|
| Надёжность | Гарантирована | Не гарантирована |
| Упорядоченность | Да | Нет |
| Установление соединения | Да (3-way handshake) | Нет |
| Overhead | Высокий (~20 байт заголовок) | Низкий (~8 байт заголовок) |
| Задержка | Выше (ACK, retransmit) | Ниже |
| Flow control | Есть | Нет |
| Congestion control | Есть | Нет |
| Broadcast/Multicast | Нет | Да |
Когда использовать TCP, а когда UDP?
TCP подходит для:
- HTTP/HTTPS — веб-запросы, REST API
- Базы данных — критично не потерять ни одной транзакции
- Email (SMTP, IMAP) — письмо должно дойти целиком
- Передача файлов (FTP, SFTP) — каждый байт важен
- SSH — удалённое управление серверами
UDP подходит для:
- Видеозвонки (Zoom, WebRTC) — лучше потерять кадр, чем получить задержку
- Online-игры — позиция игрока обновляется 60 раз в секунду, старая уже не нужна
- DNS — маленький запрос, быстрый ответ
- Мониторинг метрик — потеря одной точки данных некритична
- Стриминг аудио/видео — деградация качества лучше остановки
💡 На интервью часто спрашивают: «Почему DNS использует UDP?». Ответ: DNS-запрос и ответ умещаются в один UDP-пакет (обычно < 512 байт). Накладные расходы TCP были бы несоразмерны. При этом если ответ не пришёл — клиент просто повторит запрос.
2 Эволюция HTTP: от 1.1 до 3
HTTP (HyperText Transfer Protocol) — основа современного веба и большинства API. За 30 лет протокол прошёл путь от простой текстовой передачи файлов до высокоэффективного мультиплексирующего транспорта.
HTTP/1.1: рабочая лошадка веба
HTTP/1.1 был стандартизирован в 1997 году и до сих пор широко используется. Его главная проблема — Head-of-Line Blocking (HOL Blocking).
В HTTP/1.1 запросы по одному TCP-соединению обрабатываются строго последовательно: пока не пришёл ответ на первый запрос, второй не отправляется.
Соединение 1: GET /style.css → ждём ответа → GET /script.js → ждём → ...
Соединение 2: GET /image.png → ждём ответа → ...
Соединение 3: GET /font.woff → ждём ответа → ...
# Браузеры открывают 6–8 параллельных соединений как обходное решение
Для смягчения проблемы появились:
- Keep-Alive: переиспользование TCP-соединений для нескольких запросов
- Pipelining: отправка нескольких запросов без ожидания ответа (плохо поддерживается)
- Domain sharding: разбивка ресурсов по поддоменам для обхода лимита соединений
HTTP/2: мультиплексирование и сжатие
HTTP/2, разработанный Google на основе протокола SPDY, стандартизирован в 2015 году. Он решил ключевые проблемы HTTP/1.1.
1. Мультиплексирование потоков
Несколько запросов и ответов передаются одновременно по одному TCP-соединению в виде независимых «потоков» (streams):
Одно TCP-соединение:
Stream 1: GET /style.css → Response: 200 OK (style.css)
Stream 3: GET /script.js → Response: 200 OK (script.js)
Stream 5: GET /image.png → Response: 200 OK (image.png)
(все три потока передаются параллельно)
2. Сжатие заголовков (HPACK)
HTTP-заголовки часто повторяются в каждом запросе (User-Agent, Cookie и т.д.). HPACK использует словарь для сжатия — экономия 85–90% объёма заголовков.
3. Server Push
Сервер может отправить ресурсы клиенту проактивно, не дожидаясь запроса:
Клиент: GET /index.html
Сервер: 200 OK (index.html) + PUSH /style.css + PUSH /app.js
4. Приоритизация
Клиент может указать приоритет потоков — например, CSS-файлы важнее изображений при первой загрузке страницы.
📌 HTTP/2 всё ещё работает поверх TCP. Это означает, что HOL Blocking не исчез полностью — теперь он на уровне TCP: потеря одного пакета блокирует все потоки, пока TCP не сделает ретрансмиссию.
HTTP/3: QUIC и будущее протокола
HTTP/3, финализированный в 2022 году, революционен: он отказывается от TCP в пользу QUIC — нового транспортного протокола поверх UDP, разработанного Google.
Ключевые преимущества HTTP/3 (QUIC):
- Устранение TCP HOL Blocking: потеря пакета в одном потоке не блокирует другие
- Более быстрое установление соединения: 0-RTT или 1-RTT вместо 3-RTT у TCP+TLS
- Встроенное шифрование: TLS 1.3 интегрировано в протокол
- Connection migration: при смене IP (WiFi → LTE) соединение не разрывается
- Независимые потоки: каждый поток имеет свой контроль потока
TCP+TLS 1.2: SYN → SYN-ACK → ACK → TLS Hello → ... = 3 RTT
TCP+TLS 1.3: SYN → SYN-ACK → ACK+TLS → ... = 2 RTT
QUIC (1-RTT): QUIC Hello+TLS → Response = 1 RTT
QUIC (0-RTT): Resume+Data → Response = 0 RTT (повторное подключение)
По данным Google, HTTP/3 используется примерно в 25% всего интернет-трафика. Все крупные CDN (Cloudflare, Fastly, Akamai) поддерживают его.
Сравнение версий HTTP
| Характеристика | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| Транспорт | TCP | TCP | UDP (QUIC) |
| Мультиплексирование | Нет | Да | Да |
| HOL Blocking | Да (HTTP) | Да (TCP) | Нет |
| Сжатие заголовков | Нет | HPACK | QPACK |
| Server Push | Нет | Да | Да |
| Шифрование | Опционально | Обычно TLS | Встроено |
| Adoption (2024) | ~30% | ~40% | ~25% |
💡 На интервью: когда нужно использовать HTTP/3? Для мобильных приложений с нестабильным соединением, для CDN-доставки медиаконтента, для финансовых приложений где важна скорость. Netflix, Google, Facebook уже используют HTTP/3 для своих мобильных клиентов.
3 Real-time коммуникация: WebSocket, SSE и Long Polling
HTTP — протокол запрос-ответ: клиент инициирует, сервер отвечает. Но многие приложения требуют обратного — чтобы сервер мог отправить данные клиенту когда угодно: чат-сообщение, уведомление, биржевая котировка. Для этого существуют три основных подхода.
Long Polling: имитация real-time поверх HTTP
Long Polling — самый простой способ получить обновления с сервера, работающий поверх обычного HTTP:
- Клиент отправляет HTTP-запрос
- Сервер не отвечает сразу, а держит соединение открытым
- Когда появляются новые данные — сервер отвечает и закрывает соединение
- Клиент немедленно отправляет следующий запрос
// Псевдокод Long Polling клиента async function longPoll(lastEventId) { while (true) { try { const response = await fetch(`/api/events?since=${lastEventId}`, { timeout: 30000 }); const events = await response.json(); lastEventId = events[events.length - 1].id; processEvents(events); } catch (e) { await sleep(1000); // backoff при ошибке } } }
Плюсы: работает везде, даже через прокси; простая реализация; не требует изменений инфраструктуры.
Минусы: высокий overhead (каждое «сообщение» — новый HTTP-запрос с заголовками); нагрузка на сервер от висящих соединений; задержка в худшем случае = timeout + RTT.
Long Polling хорошо работал на заре веба (ранние версии Gmail, Facebook Chat), но сегодня вытеснен WebSocket и SSE.
Server-Sent Events (SSE): однонаправленный поток
SSE — стандарт W3C для однонаправленных обновлений от сервера к клиенту. Работает поверх обычного HTTP, но держит соединение постоянно открытым:
# Сервер отправляет события в формате text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
data: {"price": 150.25, "symbol": "AAPL"}
data: {"price": 150.30, "symbol": "AAPL"}
event: alert
data: {"message": "Price target reached"}
id: 42
// Клиент на JavaScript const es = new EventSource('/api/stock-feed'); es.onmessage = (event) => { const data = JSON.parse(event.data); updateChart(data); }; es.addEventListener('alert', (event) => { showNotification(event.data); });
Ключевые особенности SSE:
- Автоматическое переподключение: браузер reconnects автоматически при разрыве
- Идентификаторы событий:
Last-Event-IDheader при reconnect - Именованные события: разные типы событий в одном потоке
- Работает поверх HTTP/2: одно соединение, множество SSE-потоков
SSE идеален для: дашбордов с метриками, ценовых тикеров, уведомлений, прогресса долгих операций. Его используют GitHub (события в репозитории) и финансовые платформы.
WebSocket: двунаправленный real-time канал
WebSocket — протокол полнодуплексной связи поверх TCP. Соединение устанавливается через HTTP Upgrade и затем становится постоянным двунаправленным каналом:
# HTTP Upgrade Handshake
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
# После этого — бинарный WebSocket-фреймингWS, не HTTP
// Пример: чат на WebSocket (Node.js + ws) const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws, req) => { const userId = getUserFromAuth(req); ws.on('message', (data) => { const msg = JSON.parse(data); // Broadcast всем в той же комнате broadcastToRoom(msg.roomId, { type: 'message', from: userId, text: msg.text, timestamp: Date.now() }); }); ws.on('close', () => { removeFromAllRooms(userId); }); });
WebSocket используется в: чатах (Slack, Discord), онлайн-играх, торговых терминалах, совместном редактировании (Google Docs), системах телеметрии.
Сравнение подходов real-time коммуникации
| Критерий | Long Polling | SSE | WebSocket |
|---|---|---|---|
| Направление | Client → Server | Server → Client | Двунаправленное |
| Протокол | HTTP | HTTP | WebSocket (поверх TCP) |
| Overhead | Высокий | Низкий | Минимальный |
| Браузерная поддержка | Везде | Везде кроме IE | Везде |
| Прокси/Firewall | Без проблем | Без проблем | Могут блокировать |
| Автореконнект | Вручную | Встроен | Вручную |
| Масштабирование | Простое | Простое | Сложнее (sticky sessions) |
| Идеально для | Legacy-системы | Уведомления, дашборды | Чаты, игры, трейдинг |
📌 WebSocket требует sticky sessions или внешнего брокера (Redis Pub/Sub) при горизонтальном масштабировании — сервер должен знать, на каком инстансе висит соединение конкретного клиента.
4 gRPC и Protocol Buffers
gRPC — высокопроизводительный RPC-фреймворк, разработанный Google и открытый в 2015 году. Он построен поверх HTTP/2 и использует Protocol Buffers как формат сериализации данных. gRPC стал де-факто стандартом для межсервисного взаимодействия в микросервисных архитектурах.
Protocol Buffers (protobuf)
Protocol Buffers — бинарный формат сериализации данных. В отличие от JSON (текстовый), protobuf компактнее и быстрее, но требует предварительного определения схемы.
// user.proto — определение схемы syntax = "proto3"; package user; message User { int64 id = 1; string name = 2; string email = 3; repeated string roles = 4; Timestamp created_at = 5; } service UserService { rpc GetUser (GetUserRequest) returns (User); rpc ListUsers (ListUsersRequest) returns (stream User); rpc CreateUser (CreateUserRequest) returns (User); rpc BatchCreateUsers (stream CreateUserRequest) returns (BatchResponse); }
Сравнение размера сериализации (один объект пользователя):
| Формат | Размер (байт) | Относительно JSON |
|---|---|---|
| JSON | ~150 байт | 100% (baseline) |
| JSON сжатый (gzip) | ~90 байт | 60% |
| Protocol Buffers | ~30 байт | 20% |
| Protocol Buffers (gzip) | ~25 байт | 17% |
Четыре типа RPC в gRPC
gRPC поддерживает четыре паттерна взаимодействия.
1. Unary RPC — классический запрос-ответ:
rpc GetUser (GetUserRequest) returns (User);
// Клиент const user = await userService.getUser({ id: 123 });
2. Server Streaming — сервер отправляет поток:
rpc ListUsers (ListUsersRequest) returns (stream User);
const stream = userService.listUsers({ page_size: 100 }); stream.on('data', (user) => process(user)); stream.on('end', () => console.log('Done'));
3. Client Streaming — клиент отправляет поток:
rpc BatchCreateUsers (stream CreateUserRequest) returns (BatchResponse);
const stream = userService.batchCreateUsers((err, response) => { console.log(`Created: ${response.created_count}`); }); users.forEach(u => stream.write({ user: u })); stream.end();
4. Bidirectional Streaming — двусторонний поток:
rpc Chat (stream ChatMessage) returns (stream ChatMessage); // Идеально для чата, игровых обновлений, совместного редактирования
Преимущества gRPC
- Производительность: protobuf на 3–10x компактнее JSON, HTTP/2 даёт мультиплексирование
- Строгая типизация: схема — это контракт, ошибки типов ловятся на этапе компиляции
- Code generation: из
.protoфайла автоматически генерируется код клиента и сервера - Поддержка 11+ языков: Go, Java, Python, C++, Rust, Kotlin и другие
- Streaming из коробки: все 4 паттерна взаимодействия
- Deadline/Timeout пропагация: таймаут передаётся по всей цепочке вызовов
Ограничения gRPC
- Ограниченная браузерная поддержка: нужен grpc-web или транскодирование
- Сложность отладки: бинарный протокол не читается без специальных инструментов
- Необходимость protoc: нужна компиляция схем перед использованием
- Менее удобен для публичных API: REST с JSON проще для внешних потребителей
gRPC используется внутри инфраструктуры Google, Netflix, Uber, Slack для взаимодействия между микросервисами, где важна производительность.
5 REST vs GraphQL vs gRPC: сравнение и выбор
Выбор API-стиля — одно из ключевых архитектурных решений. REST, GraphQL и gRPC решают разные задачи, и важно понимать, когда применять каждый из них.
REST (Representational State Transfer)
REST — архитектурный стиль, основанный на ресурсах и стандартных HTTP-методах. Создан Роем Филдингом в 2000 году.
Ключевые принципы REST:
- Ресурсо-ориентированность:
/users,/orders,/products— каждый URL это ресурс - Stateless: каждый запрос самодостаточен, сервер не хранит состояние сессии
- HTTP-методы как операции: GET (чтение), POST (создание), PUT/PATCH (изменение), DELETE
- Uniform Interface: стандартный интерфейс для всех ресурсов
# Типичный RESTful API
GET /api/v1/users # Список пользователей
GET /api/v1/users/123 # Конкретный пользователь
POST /api/v1/users # Создать пользователя
PATCH /api/v1/users/123 # Обновить частично
DELETE /api/v1/users/123 # Удалить
# Связанные ресурсы
GET /api/v1/users/123/orders # Заказы пользователя
GET /api/v1/users/123/orders/456 # Конкретный заказ
Проблемы REST:
- Over-fetching: получаем больше данных, чем нужно
- Under-fetching / N+1 problem: нужно несколько запросов для получения связанных данных
- Versioning: управление версиями API при эволюции схемы
GraphQL: гибкий запрос данных
GraphQL — язык запросов для API, разработанный Facebook в 2012 году. Клиент точно указывает, какие данные ему нужны.
# GraphQL запрос — клиент берёт ровно то, что нужно query GetUserProfile($id: ID!) { user(id: $id) { name email recentOrders(limit: 5) { id totalAmount status items { productName quantity } } } } # Один запрос — пользователь + последние 5 заказов + товары в них
Преимущества GraphQL:
- Нет over/under-fetching: клиент берёт ровно те поля, которые нужны
- Один endpoint:
/graphqlвместо множества REST-эндпоинтов - Сильная типизация: schema + introspection = автодокументация
- Real-time subscriptions: встроенная поддержка WebSocket-подписок
Проблемы GraphQL:
- N+1 запросы: без DataLoader каждый resolver делает отдельный запрос к БД
- Сложность кэширования: нет стандартных HTTP-кэшей (у каждого запроса уникальное тело)
- Rate limiting: сложнее ограничить (один запрос может быть очень тяжёлым)
- Over-complexity для простых API: избыточен для CRUD-сервисов
Матрица выбора
| Критерий | REST | GraphQL | gRPC |
|---|---|---|---|
| Использование | Публичные API, CRUD | Сложные клиенты, мобайл | Межсервисное взаимодействие |
| Производительность | Средняя | Средняя | Высокая |
| Гибкость запросов | Низкая | Высокая | Средняя |
| Документация | OpenAPI/Swagger | Schema + Introspection | Protobuf + gRPC reflection |
| Браузер | Нативно | Нативно | Нужен grpc-web |
| Кэширование | HTTP-кэш | Сложно | Нет стандарта |
| Streaming | Нет | Subscriptions | Да (4 паттерна) |
| Кто использует | Twitter API, GitHub API | Facebook, GitHub v4, Shopify | Google внутри, Netflix, Uber |
Гибридные архитектуры
В реальных системах часто используют несколько протоколов одновременно:
Mobile/Web клиенты
↓ REST / GraphQL (публичный API)
API Gateway
↓ gRPC (внутренние вызовы)
User Service ←→ Order Service ←→ Payment Service
↓ Kafka (асинхронные события)
Analytics Service / Notification Service
Такой подход даёт лучшее из обоих миров: удобный REST/GraphQL для клиентов и высокопроизводительный gRPC внутри.
💡 На интервью: «Как бы вы спроектировали API для мобильного приложения?». Хороший ответ: GraphQL или специализированный REST с BFF (Backend for Frontend) паттерном, чтобы минимизировать количество запросов и объём трафика — критично для мобильных клиентов.
6 Архитектурные паттерны сетевой коммуникации
Синхронная vs Асинхронная коммуникация
Синхронная коммуникация (request-response):
- Клиент ждёт ответа сервера
- REST, gRPC Unary, GraphQL Query/Mutation
- Простота понимания, но создаёт coupling между сервисами
- Каскадные сбои: если один сервис падает, падают все, кто его вызывает
Асинхронная коммуникация (message-based):
- Сервисы обмениваются через очередь сообщений (Kafka, RabbitMQ)
- Отправитель не ждёт обработки
- Temporal decoupling: сервисы могут быть недоступны независимо
- Сложнее отлаживать (трассировка через несколько сервисов)
API Gateway паттерн
API Gateway — единая точка входа для всех клиентов. Он берёт на себя кросс-функциональные задачи:
- Аутентификация и авторизация
- Rate limiting и квоты
- SSL termination
- Логирование и трассировка
- Трансформация запросов (например, REST → gRPC)
- Circuit breaking
Client → [API Gateway] → User Service (gRPC)
→ Order Service (gRPC)
→ Product Service (REST)
→ Legacy Service (SOAP → REST translation)
Популярные решения: Kong, AWS API Gateway, Nginx, Envoy, Traefik. Netflix и Amazon используют собственные реализации, обрабатывающие миллионы запросов в секунду.
Service Mesh
Service Mesh — инфраструктурный слой для управления коммуникацией между микросервисами, реализованный через sidecar-прокси (обычно Envoy):
- mTLS между сервисами (шифрование + аутентификация)
- Distributed tracing без изменения кода приложения
- Circuit breaking и retry на уровне инфраструктуры
- Traffic splitting для canary deployments
Istio, Linkerd, Consul Connect — наиболее популярные реализации. Используются в Lyft, eBay, Google.
7 Числа, которые нужно знать
На System Design интервью важно быстро оценивать характеристики сети.
| Операция | Латентность | Примечание |
|---|---|---|
| L1 Cache reference | 0.5 нс | Для контекста CPU vs Network |
| L2 Cache reference | 7 нс | |
| RAM reference | 100 нс | |
| SSD random read | 150 мкс | |
| HDD seek | 10 мс | |
| Внутри датацентра (сеть) | 0.5 мс | Сервер → Сервер |
| Москва → Лондон (RTT) | ~50 мс | |
| Москва → Нью-Йорк (RTT) | ~100 мс | |
| Москва → Токио (RTT) | ~200 мс | |
| TCP handshake | 1 RTT | Плюс TLS: ещё 1–2 RTT |
| DNS lookup (кэш) | < 1 мс | |
| DNS lookup (recursive) | 50–100 мс | |
| HTTP/1.1 request (LAN) | ~1 мс | |
| gRPC request (LAN) | ~0.5 мс | Protobuf + HTTP/2 |
📌 Правило: один датацентр — задержки в сотых долях миллисекунды. Между регионами — десятки миллисекунд. Это фундаментально влияет на проектирование — синхронные вызовы через регионы (inter-region) нужно минимизировать.
Итоги главы
В этой главе мы рассмотрели ключевые протоколы и паттерны сетевой коммуникации:
- TCP гарантирует надёжность, UDP — минимальную задержку. Выбор зависит от требований к потерям данных.
- HTTP/2 решил проблему одного запроса за раз через мультиплексирование. HTTP/3 (QUIC) устранил TCP HOL Blocking.
- WebSocket — лучший выбор для двунаправленного real-time (чаты, игры). SSE проще для однонаправленных обновлений.
- gRPC + protobuf обеспечивают высокую производительность и строгую типизацию для межсервисного взаимодействия.
- REST хорош для публичных API, GraphQL для гибких клиентов, gRPC для внутренних сервисов.
- Реальные системы используют несколько протоколов: REST/GraphQL снаружи, gRPC внутри, Kafka для асинхронных событий.
Вопросы для самопроверки
- В чём разница между TCP HOL Blocking и HTTP/2 HOL Blocking? Как HTTP/3 решает эту проблему?
- Вы проектируете систему доставки уведомлений для мобильного приложения. Какой протокол выберете и почему?
- Почему gRPC сложнее использовать для публичных API, чем REST? Как это обходят на практике?
- Объясните N+1 проблему в GraphQL. Как её решает DataLoader?
- У вас чат на 10 млн пользователей. Почему WebSocket требует sticky sessions? Как масштабировать без sticky sessions?
- Когда имеет смысл использовать Long Polling в 2024 году?
- Вы разрабатываете мобильное приложение на слабом 3G-соединении. Какой API-стиль предпочтёте (REST/GraphQL/gRPC) и почему?
Дополнительные ресурсы
Спецификации и официальная документация:
- RFC 9114 — HTTP/3 спецификация
- RFC 9000 — QUIC транспортный протокол
- gRPC официальная документация: grpc.io
- GraphQL спецификация: spec.graphql.org
Статьи и блоги:
- "HTTP/3 Explained" — Daniel Stenberg (создатель curl)
- "How we moved our API to gRPC" — Cloudflare Blog
- "GraphQL: A data query language" — Facebook Engineering Blog
- "Real-time messaging at Slack" — Slack Engineering Blog
Практические задания:
- Реализуйте простой gRPC-сервис на Go или Python
- Сравните производительность REST vs gRPC с помощью
wrkилиghz - Попробуйте WebSocket через Socket.IO, реализовав простой чат