Технологии

API-шлюз (API Gateway)

Как использовать API-шлюз для решения широкого круга задач по System Design

Что такое API-шлюз?

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

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

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

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

Таким образом, шлюз API представляет собой относительно небольшой компонент, выполняющий важную роль в современных архитектурах. Далее мы рассмотрим ключевые концепции, которые вам нужно усвоить перед собеседованием по System Design.

Основные задачи шлюза API

Главная задача шлюза API заключается в маршрутизации запросов - определении, какой внутренний сервис должен обрабатывать каждый поступивший запрос. Однако это лишь одна из функций шлюза.

Забавно, но довольно часто кандидаты на собеседовании по System Design используют API-шлюз и подчеркивают, что он выполнит всю работу промежуточных слоев (middleware), но никогда не упоминают основную причину, почему он нужен - маршрутизацию запросов.

Сегодня шлюзы API используются для решения кросс-функциональных задач или промежуточных действий вроде аутентификации, ограничения скорости запросов (rate limiting), кэширования, завершения SSL соединений (SSL termination) и других операций.

Последовательность обработки запроса

Давай пройдемся по запросу от начала до конца. Входящие запросы поступают в шлюз API от клиентов, обычно через HTTP, хотя возможны и другие протоколы, такие как gRPC. Затем шлюз применяет любые промежуточные слои (middleware), которые вы настроили, и направляет запрос соответствующему бэкенд-сервису.

  1. Проверка запроса
  2. Шлюз API применяет промежуточную обработку (аутентификация, ограничение запросов и др.).
  3. Шлюз API направляет запрос соответствующему бэкенд-сервису.
  4. Бэкенд-сервис обрабатывает запрос и возвращает ответ.
  5. Шлюз API преобразует ответ и возвращает его клиенту.
  6. Опционально кэширует ответ для будущих запросов.
Обработка запроса через API шлюз

Давайте подробнее рассмотрим каждый этап.

1. Проверка запроса

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

Эта ранняя проверка важна, потому что помогает выявить очевидные проблемы до того, как они дойдут до бэкенд-сервисов. Например, если мобильное приложение отправляет неправильно сформированный JSON или забывает включить обязательный ключ API, нет смысла направлять этот запрос дальше в систему. Шлюз может быстро отклонить его и вернуть информативное сообщение об ошибке, экономя бэкенд-сервисам время и ресурсы, которые были бы потрачены впустую на запросы, заведомо обреченные на неудачу.

2. Обработка промежуточных этапов (middleware)

Шлюз API применяет настроенные вами промежуточные этапы (middlewares), такие как аутентификация, ограничение частоты запросов, завершение SSL соединений, ведение журнала трафика и многое другое:

  • Аутентификация запросов с использованием токенов JWT
  • Ограничение частоты запросов для предотвращения перегрузки (rate limiting)
  • Завершение соединений SSL
  • Логирование и мониторинг трафика
  • Сжатие ответов
  • Обработка заголовков CORS
  • Контроль белого/черного списка IP-адресов
  • Проверка размеров запросов
  • Обработка таймаутов ответов
  • Версионирование API
  • Регулирование скорости трафика (throttling)
  • Интеграция с обнаружением сервисов (service discovery)

Среди перечисленных функций наиболее популярными и релевантными для собеседований по System Design являются аутентификация, ограничение частоты запросов и белый/черный список IP-адресов. Если вы решите упомянуть промежуточные обработки (middleware), убедитесь, что делаете это целенаправленно и не тратите на это слишком много времени.

Совет при использовании API-шлюза в вашем дизайне: просто скажите: "Я добавлю API-шлюз для обработки маршрутизации и базовых промежуточных обработок (middleware)" и продолжайте далее.

3. Маршрутизация

Основываясь на правилах маршрутизации, шлюз направляет запрос соответствующему внутреннему сервису. Эти правила часто зависят от пути URL, метода HTTP-запроса, параметров запроса, заголовков и их комбинаций:

  • URL пути (например /users/* - маршрутизирует к сервису Users)
  • HTTP методы (например, GET, POST, и так далее)
  • Параметры запроса (query parameters)
  • Заголовки (request headers)

Например, вот как может выглядеть простая конфигурация:

routes:
  - path: /users/*
    service: user-service
    port: 8080
  - path: /orders/*
    service: order-service
    port: 8081
  - path: /payments/*
    service: payment-service
    port: 8082

4. Коммуникация с внутренним сервисом

После преобразования запроса шлюз отправляет его нужному внутреннему сервису. Хотя большинство сервисов общаются посредством HTTP, иногда ваши внутренние сервисы могут использовать другой протокол, например gRPC, для внутреннего взаимодействия. Когда такое случается, API-шлюз способен сделать преобразование между протоколами, хотя на практике это встречается относительно редко. Это удобно, поскольку позволяет вашим сервисам использовать тот протокол или формат, который является наиболее эффективным, без необходимости клиентов знать об этом.

5. Преобразование ответа

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

Например:

// Клиент отправляет HTTP GET запрос
GET /users/123/profile

// API-шлюз преобразовывает запрос в gRPC вызов
userService.getProfile({ userId: "123" })

// Шлюз преобразовывает gRPC ответ в JSON и возвращает клиенту по HTTP
{
  "userId": "123",
  "name": "John Doe",
  "email": "john@example.com"
}

6. Кеширование

Перед отправкой ответа клиенту шлюз может дополнительно кэшировать ответ. Это полезно для часто запрашиваемых данных, которые редко меняются и, что важно, не зависят от конкретного пользователя. Если ожидается, что определенный запрос API вернет одинаковый результат для заданных параметров, имеет смысл его кэшировать.

API-шлюз также может реализовать различные стратегии кэширования. Например:

  • Полное кэширование ответов: кэшируются полные ответы для часто используемых API эндпоинтов
  • Частичное кэширование: кэшируются отдельные части ответов, которые редко изменяются
  • Инвалидация кэша: использование истечения срока хранения (TTL) или основанной на событиях инвалидации

Во всех случаях ответ можно либо хранить в памяти, либо использовать распределенный кэш, такой как Redis.

Масштабирование шлюза API

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

Горизонтальное масштабирование

Наиболее простой способ увеличения производительности шлюза API - горизонтальное масштабирование. API-шлюзы обычно не хранят состояние (stateless), что делает их идеальными кандидатами для горизонтального масштабирования. Добавление новых экземпляров шлюза за балансировщиком нагрузки позволяет равномерно распределять входящие запросы.

Хотя API-шлюзы преимущественно известны своей функциональностью маршрутизации и промежуточными обработками (middleware), они зачастую включают возможности балансировки нагрузки. Однако важно понимать различия: Клиент-шлюз балансировка (Client-to-Gateway Load Balancing) - обычно эта задача решается специализированным балансировщиком нагрузки, расположенным перед экземплярами вашего API-шлюза (например, AWS ELB или NGINX). Шлюз-сервис балансировка (Gateway-to-Service Load Balancing) - сам API-шлюз может осуществлять балансировку нагрузки между несколькими экземплярами бэкенд-сервисов.

Обычно в ходе интервью можно абстрагироваться от этих деталей. Достаточно нарисовать один блок для обозначения "API-шлюза и балансировщика нагрузки". Не нужно погружаться тут в подробности (если только об этом прямо не просит интервьюер), так как они скорее отвлекают внимание от основной функциональности вашей системы.

Глобальное распределение

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

Для этого потребуется:

  1. Развертывание по регионам - экземпляры шлюза развернуты в нескольких географических регионах
  2. Маршрутизация на основе DNS - используем GeoDNS для направления пользователей к ближайшему шлюзу
  3. Синхронизация конфигурации - нужно проверить, что правила маршрутизации и политики согласованы по всем регионам

Популярные API-шлюзы

Рассмотрим некоторые наиболее распространенные варианты реализации API-шлюза.

Управляемые облачные сервисы

Облачные провайдеры предлагают полностью управляемые решения для API-шлюзов, которые интегрируются с экосистемами провайдера. Такие решения просты в эксплуатации, однако стоят дороже собственных решений.

Примеры: шлюзы API от AWS, Google Cloud Platform и Microsoft Azure:

  1. AWS API Gateway

    • Бесшовная интеграция с сервисами AWS
    • Поддержка REST и WebSocket
    • Встроенные функции, такие как:
      • Ограничение частоты запросов (throttling)
      • Ключи API и планы использования
      • Интеграция с AWS Lambda
      • Мониторинг CloudWatch
  2. Azure API Management

    • Хорошая поддержка OAuth и OpenID Connect
    • Настройка на основе политик
    • Портал разработчика для документации API
  3. Google Cloud Endpoints

    • Глубокая интеграция с сервисами Google Cloud Platform (GCP)
    • Хорошая поддержка gRPC
    • Автоматическое создание документации OpenAPI

Open Source решения

Некоторые команды предпочитают больше контроля над своими системами или работают вне облачных платформ. В таком случае можно выбрать одно из открытых решений, таких как Kong, Tyk или Express Gateway.

  1. Kong

    • Создан на основе NGINX
    • Обширная экосистема плагинов
    • Поддерживает как традиционные развертывания, так и развертывания с сетью служб (service mesh)
  2. Tyk

    • Родная поддержка GraphQL
    • Встроенная аналитика API
    • Возможности мульти дата центров (multi-data center)
  3. Express Gateway

    • Основан на JavaScript/Node.js
    • Легковесный и дружелюбный к разработчикам
    • Хорошо подходит для микросервисов на Node.js

Эти инструменты гибки в настройке и позволяют интегрироваться с существующими инфраструктурами, такими как Kubernetes и Docker Swarm.

Когда стоит использовать шлюз API?

Используйте шлюз API, когда ваша система построена на архитектуре микросервисов, и откажитесь от него, если имеете дело с простой архитектурой клиент-сервер.

Архитектуры микросервисов практически всегда требуют наличия шлюза API для эффективного взаимодействия между клиентами и серверами. В противном случае клиентам придется напрямую обращаться ко множеству отдельных сервисов, усложняя систему и повышая зависимость компонентов друг от друга.

Однако помните, что введение шлюза API добавляет сложность там, где ее можно избежать. Например, в простых монолитных приложениях или системах с одним типом клиента необходимость в шлюзе API минимальна.

Мы упоминали это ранее, но хочется еще раз сказать предельно ясно. Хоть и важно понимать каждый компонент, который вы используете в своем дизайне - API-шлюз не является самой интересной частью. Существует вероятность того, что вы совершаете ошибку, тратя слишком много времени на обсуждение деталей API-шлюзов.

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

Войдите чтобы отмечать прогресс