API Gateway — это не «ещё один nginx с модными словами». Это входная дверь в вашу распределённую систему, и именно от неё зависит, попадёт клиент к вам в дом или будет час стучать в закрытое окно.
Если у вас один сервис — забудьте про шлюз, вам он не нужен. Если у вас три сервиса и три фронтенда, и каждый фронт ходит сразу во все три — поздравляю, вы только что изобрели «архитектуру звезды» и скоро столкнётесь с тем, что любая мелкая правка авторизации требует деплоя пяти приложений (а ещё мобильного релиза, который ревьюится неделю).
В этом посте разберём, что такое API Gateway, чем он не является, какие задачи на него вешать можно и нужно, какие — нельзя категорически, и как его спроектировать так, чтобы шлюз не превратился в очередной распределённый монолит. Если вы пока не определились с архитектурным фундаментом, держите рядом сравнение монолита, микросервисов и модульного монолита — без этого контекста часть решений ниже будет звучать абстрактно.
Что такое API Gateway простыми словами
API Gateway — компонент, который принимает входящие запросы от клиентов, выполняет сквозные задачи (аутентификация, лимитирование, логирование, маршрутизация, трансформация) и проксирует их в нужные внутренние сервисы.
Если очень грубо — это администратор бизнес-центра. Курьер с пиццей не ломится сразу в офис на 14-м этаже: он сначала подходит к стойке, говорит, к кому пришёл, его проверяют, выписывают пропуск, объясняют, на каком лифте ехать. Шлюз делает ровно то же самое для HTTP-запросов: одна точка входа, единые правила, унифицированный контроль.
Важно: API Gateway — это паттерн, а не конкретный продукт. Реализаций много (Kong, Nginx с lua, Envoy, AWS API Gateway, Apigee, Traefik, Spring Cloud Gateway), и под капотом они могут быть устроены по-разному — от L7-прокси с конфигом до полноценного приложения с плагинами и хранилищем политик.
Чем шлюз не является
- Не балансировщик. Балансировщик распределяет нагрузку между инстансами одного сервиса. Шлюз — между разными сервисами по правилам маршрутизации. Иногда роли совмещают, но смешивать в голове нельзя.
- Не сервис-меш. Service mesh (Istio, Linkerd) занимается трафиком между внутренними сервисами. Gateway — только трафиком с улицы внутрь. Эти штуки часто живут вместе.
- Не бизнес-логика. Если в шлюзе появилась логика расчёта скидки — это уже не шлюз, это микросервис с плохой репутацией.
Зачем нужен API Gateway: какие проблемы он решает
Без шлюза каждый клиент знает про каждый сервис: его адрес, версию, особенности авторизации. Это работает ровно до момента, пока сервисов меньше пяти, а клиент один. Дальше начинается беда.
Шлюз нужен, когда хочется одно из следующего (а лучше всё сразу):
- Единая точка аутентификации. Один раз проверить токен на входе — а не в каждом из пятнадцати сервисов. Подробнее про сами токены — в посте про OAuth и JWT для REST API.
- Скрыть внутреннюю топологию. Клиенту не нужно знать, что заказ — это шесть микросервисов под капотом. Сегодня шесть, завтра восемь. Внешний контракт не должен от этого дёргаться.
- Единые сквозные функции. Логирование, метрики, трейсинг, rate limiting, CORS, gzip — всё это должно жить в одном месте, а не размножаться по сервисам копипастой.
- Разные клиенты — разные нужды. Мобилке нужен один JSON, веб-кабинету — другой, партнёрской интеграции — третий. Шлюз умеет агрегировать и трансформировать ответы (паттерн BFF — Backend for Frontend).
- Контроль и квотирование. Партнёр платит за 1000 запросов в минуту? Это решается на шлюзе одной строчкой конфига, а не доработкой каждого сервиса.
Как спроектировать API Gateway: схема и потоки
Разберёмся, как в принципе устроен поток запроса. Тут важна не картинка ради картинки, а понимание, где принимается каждое решение и что может пойти не так на каждом шаге.
На диаграмме видно три зоны ответственности. Жёлтая — то, что вообще не наша головная боль на уровне приложения: CDN режет ботов и кэширует статику, балансировщик выбирает живой инстанс шлюза. Зелёная — собственно шлюз: пять стадий, через которые проходит каждый запрос. Фиолетовая — доменные сервисы, которые не должны знать ничего про токены, лимиты и CORS.
Стадии обработки запроса
- Аутентификация. Шлюз достаёт
Authorization: Bearer ..., валидирует подпись JWT (по ключу из IdP) или дёргает introspection-endpoint. Невалидный токен — 401, и дальше в систему запрос не идёт. Сервисы получают уже распарсенный контекст пользователя в заголовкеX-User-Id. - Rate limiting. Проверяется по ключу (user_id, api_key, IP). Лимит превышен — 429, Retry-After в заголовке. Без шлюза вы будете писать этот код в каждом сервисе и обязательно где-нибудь забудете.
- Маршрутизация. По пути и заголовкам шлюз решает, кому отдать запрос. Адрес сервиса берётся из service registry или статического конфига. Версии API (
/v1/,/v2/) — тоже сюда. - Трансформация / агрегация. Иногда мобильному клиенту нужен один ответ, склеенный из трёх сервисов. Это паттерн BFF. Только не превращайте шлюз в god-object — об этом ниже.
- Логирование, трейсинг, метрики. Один
X-Request-Id, который шлюз пробрасывает дальше, спасёт вас на проде в три часа ночи.
API Gateway vs прямые вызовы клиента: таблица сравнения
Чтобы стало совсем ясно — собрал в одну таблицу разницу подходов. Колонка «Без шлюза» — это про систему, где клиент ходит во все сервисы напрямую. Знакомая многим картина.
| Аспект | Без шлюза (прямые вызовы) | С API Gateway |
|---|---|---|
| Аутентификация | Дублируется в каждом сервисе | Одна точка, один кодовый путь |
| Изменение топологии | Ломает контракт клиента | Прозрачно для клиента |
| Rate limiting | Размазан или отсутствует | Централизованный, по политикам |
| Логи и трейсинг | Несвязные потоки | Единый request_id сквозь систему |
| Версионирование API | Каждый сервис сам по себе | Управляется маршрутами шлюза |
| Поддержка разных клиентов | Один контракт на всех | BFF под мобилку / веб / партнёров |
| Точка отказа | Распределённая | Шлюз — критичный SPOF |
| Latency | Минимальный | +1 hop (обычно 1–5 мс) |
Последние две строки — это не «минус шлюза», это плата, про которую нужно помнить. Если шлюз лёг — лежит вся внешняя поверхность. Поэтому он всегда в HA-конфигурации, минимум два инстанса за балансировщиком, желательно в разных AZ.
Антипаттерны: как не надо делать API Gateway
Есть пара вещей, на которых обжигались примерно все, кто строил шлюз с нуля. Расскажу, чтобы вы обожглись пореже.
God Gateway
Это когда в шлюз начинают засовывать всё подряд: бизнес-валидации, расчёт цены, маппинг состояний заказа. Через полгода у вас полноценный микросервис, который называется «gateway» и через который проходит 100% трафика. Деплой — раз в неделю с молитвой. Любая ошибка ронит весь продукт.
Правило простое: на шлюзе живёт только сквозная инфраструктурная логика. Если для добавления фичи нужно править шлюз — это не фича для шлюза, это фича для домена.
Один шлюз на всех клиентов
Заманчиво сделать один универсальный API и проксировать его всем. На практике у мобильного приложения, веб-кабинета и B2B-партнёра разные требования: к payload, к версионированию, к лимитам, к схеме авторизации.
Лучшее решение — несколько шлюзов под разные аудитории (внешний публичный, внутренний для админки, отдельный для партнёров). Каждый со своей политикой.
Синхронная агрегация поверх десяти сервисов
Когда шлюз для одного запроса вызывает десять backend-ов и склеивает ответы. На бумаге красиво, на проде это значит, что задержка ответа = max задержка по всем сервисам, а доступность = произведение доступностей. Если каждый сервис даёт 99.9%, десять синхронных вызовов — это 99%. Минус 8 часов в год.
Если правда нужно агрегировать много данных — отдавайте это либо асинхронно (см. событийную архитектуру и предсобранные read-модели), либо выносите в отдельный BFF-сервис, не валите на шлюз.
Шлюз без таймаутов и circuit breaker
Один медленный сервис — и шлюз превращается в очередь зависших соединений. Через минуту встают все клиенты, потому что треды кончились. Таймауты — обязательны, circuit breaker — желателен, retry с экспоненциальной задержкой — только если запрос идемпотентный.
Итого: когда внедрять и когда подождать
Шлюз — это инвестиция. Он окупается, когда у вас минимум 3–5 сервисов и больше одного типа клиента. На старте, когда у вас один монолит и один фронт, ставить шлюз — это карго-культ: работа есть, профита нет, дополнительный hop по latency и +1 компонент в SRE-карте.
Но когда количество сервисов начинает расти — а оно начинает расти быстрее, чем хотелось бы — шлюз превращается из «модной штуки из доклада» в банальную необходимость. Вопрос только в том, спроектируете вы его осознанно или вырастите случайно из nginx-конфига, который никто не помнит, кто и зачем писал.
PS. Самое удивительное: половина проектов внедряет API Gateway не потому, что он действительно нужен, а потому что «у соседей есть». Если у вас три эндпоинта и один React-фронт — вам не нужен Kong. Вам нужно дописать тесты. Работает — не трогай.