Экспресс-курс · No. 05

Две программы могут сотрудничать, только если договорятся о правилах — что слать, в какой форме и кто когда говорит. Протокол — это и есть договор, а выбрать правильный — значит выбрать, как пойдёт разговор.

Только суть · Один образ на протокол · Примеры важнее RFC

§ 01

Протокол — это согласованный набор правил, по которым две программы разговаривают: как выглядит сообщение и кто что может сказать и когда. Правильные правила — и совершенно разные системы понимают друг друга.

Протокол — это язык плюс этикет

Два дипломата без общего родного языка всё равно договариваются — потому что заранее условились об одном языке и о порядке, кто говорит. Сломай любое — и это просто шум.

Для двух программ протокол фиксирует и то, и другое: формат сообщения (чтобы каждая сторона его прочитала) и хореографию (кто говорит первым, когда приходит ответ, как всё заканчивается). HTTP, gRPC, WebSocket — это всего лишь разные договорённости об этих двух вещах. Работа одна, манеры разные.

Протоколы устроены слоями

Письмо: почта везёт конверт и не интересуется, что внутри; язык на странице — это между тобой и читателем. Две отдельные договорённости, сложенные стопкой.

Снизу лежит транспортный протокол (TCP или UDP), который просто переносит байты между машинами. Сверху — прикладной протокол (HTTP, gRPC, MQTT), который придаёт этим байтам смысл. Обычно ты работаешь наверху — но когда становится медленно или нестабильно, нижний слой даёт о себе знать, так что полезно помнить, что он там.

Каждый протокол отвечает на три вопроса

Разговору нужно уладить три вещи: кто начинает, на каком языке и говорите ли вы по очереди или оба сразу.

Кто инициирует — клиент спрашивает или сервер может пушить? Какую форму принимают данные — текстовый JSON, компактный бинарь, непрерывный поток? Тайминг — один выстрел или долго живущая линия, где говорить может любая сторона? Почти любое различие между протоколами — это разный ответ на эти три вопроса.

Лучшего протокола нет — есть подходящий

Ты не пошлёшь приглашение на свадьбу по рации и не будешь координировать спасательную операцию почтой.

Протокол настроен под форму разговора: редкий и надёжный или постоянный и быстрый; один-на-один или один-ко-многим; читаемый человеком или как можно меньше. Мастерство не в том, чтобы знать новейший протокол, — а в том, чтобы подобрать протокол под то, как сторонам реально нужно говорить.

Протокол — это договор. Выбирай его по форме разговора, а не по тому, что сейчас в моде.

§ 02

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

TCP: надёжная линия с гарантией доставки

Телефонный звонок, где собеседник после каждой фразы говорит «принял» — медленнее, зато ничего не теряется и не путается.

TCP устанавливает соединение и гарантирует, что каждый байт дойдёт по порядку, переотправляя потерянное. Это рабочая лошадка под вебом, почтой и большинством API. Ты немного платишь установкой и задержкой за обещание, что данные полны и верны — обычно отличная сделка.

UDP: отправил и забыл — быстро и с потерями

Крик через шумную комнату — быстро, но если слово не расслышали, ты не останавливаешься его повторить, а идёшь дальше.

UDP шлёт пакеты без гарантии доставки и порядка. Звучит плохо — пока тебе не нужна скорость важнее точности: видеозвонки, живые игры, DNS-запросы — где потерянный кадр лучше заминки в ожидании переотправки. (HTTP/3 ниже построен на нём.)

HTTP: язык запрос-ответ всего веба

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

HTTP едет поверх TCP и задаёт привычный запрос/ответ с глаголами (GET, POST, PUT, DELETE) и кодами статуса (200, 404, 500). Он без состояния — каждый запрос сам по себе — и именно это позволяет вебу масштабироваться до миллиардов вызовов. Почти любой API, которого ты коснёшься, говорит на нём.

HTTPS и TLS: то же самое, но запечатанное

То же письмо, но теперь в защищённом от вскрытия конверте с проверенной подписью отправителя снаружи.

TLS оборачивает HTTP (и другие протоколы) в шифрование и идентичность, чтобы никто посередине не прочитал и не подделал трафик. Это та самая «S» в HTTPS — и больше не опция: браузеры, сторы и API его подразумевают. Если данные покидают твою машину, они должны быть внутри TLS.

HTTP/1.1 → 2 → 3: та же труба, только шире

Однополосная дорога, ставшая многополосной трассой, — а потом перестроенная так, что одна заглохшая машина больше не блокирует все полосы.

HTTP/1.1 обрабатывал по одному запросу за раз на соединение; HTTP/2 мультиплексирует много по одному; HTTP/3 переезжает на QUIC (поверх UDP), чтобы убить «head-of-line blocking», открывать соединения примерно за один круг и пережить переключение телефона с Wi-Fi на сотовую сеть, не оборвавшись. Те же запросы, что ты и так пишешь, — просто более быстрая дорога под ними.

Ты строишь наверху стека, но низ решает, что возможно. И шифрование — это не слой, который прикручивают потом.

§ 03

Бóльшая часть общения «приложение — сервер» и «сервис — сервис» — это «задай вопрос, получи ответ». Доминируют три протокола, и каждый по-своему меняет простоту на мощь.

REST: ресурсы через обычные глаголы HTTP

Хорошо организованная библиотека, где у каждой книги фиксированный адрес, и везде ты используешь одни и те же четыре действия: посмотреть, добавить, изменить, убрать.

REST моделирует всё как ресурсы по URL (/users/42), над которыми действуют глаголами HTTP, обычно возвращая JSON. Просто, кэшируемо, отлаживается в браузере и понятно везде — дефолт для публичных API. Слабые места: ты часто получаешь больше данных, чем нужно, или вынужден делать несколько вызовов, чтобы собрать один экран.

GraphQL: запрашивай ровно те поля, что нужны

Шведский стол, где вместо фиксированных комплексных меню ты подаёшь кухне точный список и получаешь ровно это — не больше и не меньше.

GraphQL даёт одну точку входа, где клиент пишет запрос ровно на те поля, что ему нужны, за один круг — снимая избыточную и недостаточную выборку REST. Блестяще, когда множество разных клиентов (веб, мобильные, партнёры) нуждаются в разных срезах сложного графа данных. Цена: кэширование сложнее, а гибкость перекладывает реальную сложность на твой сервер.

gRPC: вызови удалённую функцию как локальную

Интерком между двумя комнатами одного здания — кратко, быстро и приватно, потому что обе стороны уже знают один и тот же сокращённый язык.

gRPC использует Protocol Buffers (компактный бинарь) поверх HTTP/2, чтобы один сервис вызывал методы другого напрямую, с типизированным контрактом и встроенным стримингом. Быстро и строго — идеально для внутреннего трафика «сервис — сервис». Компромиссы: он не дружит с браузером из коробки, а бинарную нагрузку не прочитать глазами, так что отлаживаешь инструментами, а не взглядом.

И старейшина: SOAP

Формальный договор в трёх экземплярах, с печатями и нотариусом — тяжело, зато однозначно.

До REST SOAP заворачивал вызовы в строгие XML-конверты с формальными контрактами. Он многословен и старомоден, но его строгость до сих пор работает в банках, платежах и корпоративных интеграциях. Новый ты вряд ли построишь — но вполне можешь столкнуться с необходимостью с ним говорить.

REST — чтобы тебя понял каждый. GraphQL — чтобы клиент выбирал. gRPC — чтобы быстро между своими сервисами.

§ 04

У обычного запрос-ответа есть пробел: сервер не может заговорить первым. Когда клиенту нужно узнавать о событиях по мере их наступления, тянешься за одним из этих.

Polling: всё спрашивать «есть что-нибудь новое?»

Ребёнок в дороге, спрашивающий «мы уже приехали?» каждые две минуты. Просто — и в основном впустую.

Самый грубый вариант: клиент перезапрашивает по таймеру. Long-polling улучшает его, удерживая каждый запрос открытым, пока реально не появится что сказать, после чего клиент тут же спрашивает снова. Работает везде и не требует особого протокола — но расточителен и тормознее настоящего потока. Нормальный запасной вариант, плохой дефолт.

Server-Sent Events: односторонний поток от сервера

Радиовещание — станция льёт тебе поток непрерывно; ты просто слушаешь, на том же канале в ответ не говоришь.

SSE держит одно HTTP-соединение открытым и даёт серверу пушить поток обновлений клиенту, с автоматическим переподключением и почти без накладных. Идеально для односторонних лент: живые счета матчей, уведомления, тикающий дашборд — и именно так LLM стримит токены в чат-интерфейс. Если клиенту не нужно отвечать по той же линии, SSE — самый простой инструмент реального времени.

WebSocket: двусторонняя линия, открытая в обе стороны

Открытая телефонная линия, оставленная снятой с рычага, — любая сторона может заговорить в тот же миг, как появилось что сказать, без перенабора.

WebSocket повышает HTTP-соединение до постоянного, полнодуплексного канала, где клиент и сервер шлют сообщения свободно, с минимальной задержкой и крошечными накладными на сообщение. Правильный инструмент, когда обе стороны говорят постоянно: чаты, мультиплеер, совместное редактирование, живая торговля. Его сложнее эксплуатировать, чем SSE, так что бери, когда тебе правда нужна двусторонность.

Webhooks: не звоните нам — мы позвоним вам

Оставить магазину свой номер, чтобы они позвонили, когда заказ приедет, — вместо того чтобы ходить проверять каждый час.

Webhook переворачивает направление: ты регистрируешь URL, а другой сервис делает на него HTTP-запрос, когда происходит событие. Пример: Stripe делает POST на твой эндпоинт в тот момент, когда платёж прошёл; GitHub пингует твой сервер на каждый пуш. Это стандартный способ сервисам уведомлять друг друга без того, чтобы кто-то сидел в цикле опроса.

Если сервер должен заговорить первым — перестань опрашивать. В одну сторону стримь через SSE, в обе — через WebSocket.

§ 05

Иногда A вообще не должен звать B напрямую. Поставь между ними брокер — и отправитель сможет отправить и забыть, пока получатель работает в своём темпе.

Идея: посредник, который держит сообщения

Почта между отправителем и получателем — ты бросаешь письмо и уходишь; они забирают его, когда готовы. Никому не нужно быть на месте в один и тот же момент.

Вместо прямого вызова отправитель передаёт сообщение брокеру, который доставляет его одному или нескольким получателям. Это развязывает их во времени (получатель может тормозить или ненадолго лежать) и в знании (отправителю не нужно знать, кто слушает). Это становой хребет устойчивых, рваных, асинхронных систем.

Очереди сообщений (AMQP / RabbitMQ): одна задача — один воркер

Наплыв заказов в забегаловке — чеки копятся на рейке, и свободный повар хватает следующий.

Очередь держит задачи, пока воркер не возьмёт одну, обработает и подтвердит. Протоколы вроде AMQP (RabbitMQ) добавляют умную маршрутизацию, повторы и гарантии доставки. Пример: бросить «отправить приветственное письмо» или «сгенерировать счёт» в очередь, чтобы веб-запрос вернулся мгновенно, а воркер занялся медленной частью.

Kafka: долговечный лог, который многие могут переиграть

Газетный архив — каждый выпуск хранится по порядку, и любое число читателей может начать откуда угодно и читать вперёд в своём темпе.

Kafka — это не столько очередь, сколько долговечный, упорядоченный лог событий, который многие потребители читают независимо и который остаётся после прочтения. Построен под огромную пропускную способность. Пример: один поток «заказ размещён» кормит аналитику, индексацию поиска и письма сразу — и каждый потребитель отслеживает свою позицию. Очередь: сообщение исчезает, как обработано. Kafka: история остаётся.

MQTT: крошечный pub/sub для ненадёжных сетей

Канал рации для парка курьерских фургонов — короткие очереди слов, мало энергии, нормально, если один ненадолго въедет в тоннель.

MQTT — это ультралёгкий протокол публикация/подписка, созданный для IoT: крошечные сообщения, минимум батареи и трафика, терпимость к нестабильным каналам. Пример: тысячи датчиков, устройств умного дома или машин, шлющих телеметрию по сотовой сети. Когда «клиент» — это дешёвый чип на слабой сети, MQTT влезает туда, где HTTP не справится.

Не заставляй вызывающего ждать медленной работы. Передай её брокеру и иди дальше.

§ 06

Протокол — это конверт; нагрузке внутри всё равно нужна форма, о которой обе стороны договорились. Этот выбор — тихий размен между читаемостью и скоростью.

JSON: читаемый, универсальный, по умолчанию

Рукописная записка, которую любой прочитает с ходу, — чуть длинновата, но декодер не нужен.

JSON — это текст, читаемый человеком и поддерживаемый везде, — тело по умолчанию для веба и REST API. Его можно глянуть в браузере и отлаживать чтением. Цена — размер и скорость разбора: он многословен и не самый быстрый — обычно нормально, иногда узкое место на масштабе.

Protocol Buffers и компания: маленькие, быстрые, бинарные

Штрихкод — тебе нечитаем, но сканируется мгновенно и упакован плотно. Чтобы раскодировать, нужна схема.

Бинарные форматы вроде Protocol Buffers (используется gRPC), плюс MessagePack и Avro, сериализуют данные по общей схеме в компактную, быструю нагрузку. Выигрыш — размер и скорость; цена в том, что глазами это не прочитать, и обе стороны должны делить схему. Оправдано для высокообъёмного, внутреннего, критичного к производительности трафика.

XML: многословный, формальный, всё ещё рядом

Юридический документ — тяжёлый от тегов и структуры, точный, и ничей не первый выбор для быстрой записки.

XML старше JSON и несёт богатую схему и валидацию ценой громоздкости. Ты встретишь его в SOAP-сервисах, форматах документов и конфигурации. Редко выбор для чего-то нового, но до сих пор несущий во множестве старых систем, с которыми придётся интегрироваться.

Текст — чтобы понимали, бинарь — чтобы было быстро. Большинство API правы, что по умолчанию выбирают читаемое.

§ 07

Ты не выбираешь один протокол на всю систему — ты выбираешь правильный для каждого разговора. Реальные приложения крутят несколько сразу.

Подбирай протокол под разговор

Ящик с инструментами: молоток, отвёртка, ключ. Ты не споришь, что лучше, — ты смотришь на работу перед тобой.

Решай по форме разговора: публичный API для многих клиентов — это REST; два внутренних сервиса, которым нужна скорость, — gRPC; клиент, выбирающий точные поля, — GraphQL; сервер, стримящий обновления, — SSE; обе стороны вживую — WebSocket; медленная фоновая работа — очередь; один сервис сообщает другому, что произошло событие, — webhook. Вопрос никогда не «какой протокол», а «какой именно — для этой линии».

Начинай с простого; тянись за экзотикой, только когда болит

Ты не прокладываешь железную дорогу, чтобы перенести один мешок муки через двор.

Большинство продуктов уезжают очень далеко на REST + JSON поверх HTTPS, плюс очередь для фоновых задач. gRPC, GraphQL, Kafka и WebSocket — это ответы на конкретные боли: задержка «сервис — сервис», голодные клиенты, огромные объёмы событий, живое взаимодействие. Бери их, когда чувствуешь именно эту боль, а не потому что звучит современно. Самый простой протокол, подходящий под разговор, обычно побеждает.

Прежде чем выбрать протокол
  • Кто начинает разговор — клиент или сервер? - Как часто и насколько большие сообщения? - Один-на-один или один-ко-многим со временем? - Должно ли это быть читаемым — или как можно меньше и быстрее? - Кто клиент — браузер, мобильное приложение, ещё один мой сервис, крошечное устройство? - Хватает ли обычного REST + JSON, прежде чем тянуться за чем-то навороченным?
Признаки, что ты перегнул
  • Ты выбрал gRPC для публичного API, который должны звать браузеры. - WebSocket там, где хватило бы кнопки «обновить». - Kafka, чтобы перегонять пару сотен сообщений в день. - GraphQL для одного клиента, который каждый раз тянет одни и те же три поля. - Бинарный формат для данных, которые тебе постоянно нужно читать и отлаживать руками.
Признаки, что ты выбрал хорошо
  • Клиент и сервер договариваются без клеящего кода, переводящего между ними. - Протокол подходит трафику — стрим для потоков, запрос-ответ для вопросов. - Ты можешь отлаживать это инструментами, которых протокол ждёт, — браузером, логами или proto-тулзами. - Шифрование включено по умолчанию, везде. - Каждый разговор использует самый простой подходящий протокол — и не более.

Современные приложения построены не на одном протоколе. Они построены на правильном протоколе для каждого разговора — и на TLS вокруг них всех.

Конец экспресс-курса · 7 глав · протоколы важнее RFC

Дальше — глубина: спецификации HTTP и TLS, документация gRPC и GraphQL, «High Performance Browser Networking» Ильи Григорика. Но прежде деталей — представь разговор. Кто говорит, как часто, как быстро и с кем. Протокол — это всего лишь договор, который делает этот разговор возможным.