🏗️ Раздел 17 · Вопрос #9

Что такое API Gateway и какие задачи он решает

Без API Gateway клиенту пришлось бы: 4. Зависеть от внутренних изменений сетевой топологии компании.


🟢 Junior Level

API Gateway (Шлюз API) — это единая точка входа (Single Point of Entry) и специализированный пограничный сервис (Edge Service), через который внешние клиенты (веб-браузеры, мобильные приложения, сторонние интеграции) взаимодействуют с внутренней микросервисной экосистемой.

Внешний мир:                       Периметр:               Внутренний контур (Private Network):
[Mobile App]  ───┐                                                 ┌──► [User Service]
                 ├──HTTPS (Public IP)──► ┌───────────────────┐    │
[Web SPA]     ───┼─────────────────────► │    API Gateway    │────┼──► [Order Service]
                 │                       │ (Netty / Envoy)   │    │
[Third-Party] ───┘                       └───────────────────┘    └──► [Payment Service]

Зачем он нужен?

Без API Gateway клиенту пришлось бы:

  1. Знать сетевые адреса десятков микросервисов и слать десятки отдельных HTTP-запросов для отрисовки одного экрана.
  2. Проходить аутентификацию в каждом сервисе отдельно.
  3. Страдать от проблем с CORS (Cross-Origin Resource Sharing).
  4. Зависеть от внутренних изменений сетевой топологии компании.

Главные задачи:

  • Маршрутизация (Routing): Перенаправление запроса /api/v1/orders в order-service.
  • Аутентификация и безопасность: Проверка JWT-токенов на входе, завершение SSL/TLS (TLS Termination), защита от DDoS.
  • Ограничение трафика (Rate Limiting): Защита сервисов от перегрузки (например, не более 100 запросов в минуту на пользователя).
  • Сквозная функциональность (Cross-Cutting Concerns): Генерация Correlation-ID, сбор метрик, логирование, CORS.

🟡 Middle Level

1. Архитектура Spring Cloud Gateway

Современный Spring Cloud Gateway построен на неблокирующем реактивном стеке (Project Reactor + Netty + Spring WebFlux).

Он состоит из трёх ключевых сущностей:

  1. Route (Маршрут): Базовый блок, включающий ID, целевой URI, набор предикатов и фильтров.
  2. Predicate (Предикат): Java Predicate<ServerWebExchange>, проверяющий соответствие запроса условиям (URL-путь, HTTP-метод, наличие заголовков, cookies).
  3. Filter (Фильтр): Модификатор входящего запроса (Pre-filter) или исходящего ответа (Post-filter).
@Configuration
public class GatewayRoutesConfig {

    @Bean
    public RouteLocator customRoutes(RouteLocatorBuilder builder,
                                     JwtAuthenticationFilter authFilter,
                                     RedisRateLimiter redisRateLimiter) {
        return builder.routes()
            .route("order-service-route", r -> r
                .path("/api/v1/orders/**")
                .and().method(HttpMethod.GET, HttpMethod.POST)
                .filters(f -> f
                    .filter(authFilter) // Проверка токена и извлечение заголовков
                    .requestRateLimiter(c -> c
                        .setRateLimiter(redisRateLimiter)
                        .setKeyResolver(exchange -> ReactiveSecurityContextHolder.getContext()
                            .map(ctx -> ctx.getAuthentication().getName())))
                    .circuitBreaker(c -> c
                        .setName("orderServiceCircuitBreaker")
                        .setFallbackUri("forward:/fallback/orders"))
                    .stripPrefix(1) // Превращает /api/v1/orders/123 в /v1/orders/123
                )
                .uri("lb://order-service") // Резолв через Service Discovery
            )
            .build();
    }
}

2. Задачи, решаемые API Gateway

[Входящий HTTP-запрос]
       │
       ▼
[1. TLS Termination]     ──► Снятие HTTPS, дешифрование на границе сети
       │
       ▼
[2. WAF & Anti-DDoS]     ──► Блокировка SQLi, XSS, бот-трафика
       │
       ▼
[3. Rate Limiter]        ──► Redis Token Bucket (429 Too Many Requests при превышении)
       │
       ▼
[4. Authentication]      ──► Валидация JWT, расшифровка claims
       │
       ▼
[5. Tracing / Headers]   ──► Генерация X-Request-ID, добавление X-User-Id: 42
       │
       ▼
[6. Protocol Conversion] ──► Преобразование HTTP/JSON во внутренний gRPC / Protobuf
       │
       ▼
[7. Reverse Proxy]       ──► Отправка в целевой под во внутреннюю сеть

🔴 Senior Level

1. Архитектурные шаблоны безопасности: Token Relay vs Phantom Token

Как безопасно передать данные об аутентификации от Gateway к внутренним микросервисам?

  1. Token Relay Pattern: Gateway проверяет подпись внешнего JWT и пробрасывает его в неизменном виде в заголовок Authorization: Bearer <token> во внутреннюю сеть.
    • Минус: Если JWT содержит избыточные данные (PII, скоупы), внутренние сервисы тратят CPU на повторную валидацию криптографии.
  2. Phantom Token Pattern (Рекомендуемый):
    • Клиент с мобильного устройства шлет Reference Token (непрозрачный UUID, случайная строка).
    • Gateway обращается в Authorization Server (Keycloak/Hydra) или кэш Redis, валидирует reference token и заменяет его на криптографически подписанный внутренний JWT-токен с нужными claims.
    • Внутренние микросервисы доверяют Gateway и быстро читают заголовки X-User-Id и X-User-Roles.
[SPA / Mobile] ──(Opaque Token: "xyz789")──► [API Gateway]
                                                   │ 1. Валидация в Keycloak/Redis
                                                   ▼ 2. Выпуск JWT
                                             [Внутренний микросервис]
                                             (Получает JWT / заголовки X-User-Id)

2. Антипаттерн «Smart Gateway» (God Gateway)

Самая разрушительная ошибка при эксплуатации API Gateway — превращение его в монолитный узел бизнес-логики:

  • Ошибки: Размещение в шлюзе бизнес-валидаций, обращений к БД, трансформации сложных JSON-схем, вызовов цепочек сервисов (API Orchestration / Heavy Aggregation).
  • Последствия: Команды микросервисов блокируют друг друга в очереди на деплой шлюза; шлюз падает от утечек памяти; CPU исчерпывается на сериализации данных.
  • Правило: Gateway должен быть «глупым роутером» (Smart endpoints, dumb pipes). Допускается только маршрутизация, фильтрация и сквозные технические аспекты. Для агрегации данных экранов используют BFF (Backend for Frontend) или GraphQL Federation.

3. API Gateway vs Kubernetes Ingress Controller vs Service Mesh Ingress

Компонент Главная зона ответственности Примеры
Kubernetes Ingress Базовый L7-роутинг трафика извне кластера K8s к внутренним сервисам (path/host routing, TLS). Nginx Ingress Controller, Traefik
API Gateway Расширенная бизнес-ориентированная безопасность (OAuth2/OIDC, Rate Limiting по ключам API, монетизация API, трансформация запросов). Kong, Spring Cloud Gateway, Apigee, KrakenD
Service Mesh Ingress (Envoy) Управление сетевым трафиком, mTLS, канареечный деплой (Canary 90/10), сбор сетевых телеметрий без привязки к приложению. Istio Ingress Gateway, Linkerd

В современных облачных архитектурах они часто объединяются: Kubernetes Gateway API на базе Envoy выполняет роли и Ingress, и API Gateway.


4 Tricky Questions

1. Почему Spring Cloud Gateway построен на реактивном стеке (Netty + WebFlux), а не на классическом сервлетном Tomcat (как Zuul 1)?

Ответ: Классический сервлетный стек (Tomcat / Zuul 1) работает по модели Thread-per-Request: каждый входящий HTTP-запрос занимает отдельный поток ОС. Шлюз является I/O-интенсивным приложением (он не выполняет тяжелых вычислений, а ждет ответов от бэкендов). При тысячах одновременных медленных запросов пул из 200–500 потоков Tomcat мгновенно исчерпывается, приводя к параличу шлюза. Spring Cloud Gateway использует неблокирующий Event Loop (Netty), где горстка потоков (равная числу ядер CPU) способна мультиплексировать десятки тысяч одновременных сетевых соединений без переключения контекста потоков ОС.

2. Что такое API Composition (Aggregation) на шлюзе и почему на высоких нагрузках это превращается в проблему?

Ответ: API Composition — это когда шлюз по одному запросу клиента параллельно вызывает 5 микросервисов (пользователь, заказы, корзина, рекомендации, уведомления), собирает их ответы в единый JSON и отдает клиенту. Проблема на высоких нагрузках:

  1. Fan-Out Latency: Время ответа шлюза становится равно времени самого медленного из 5 сервисов (Tail Latency).
  2. Память и сборка мусора (GC): Шлюз вынужден десериализовать и удерживать в оперативной памяти гигантские графы объектов перед склейкой, что провоцирует Stop-the-World паузы GC. Решение: Перенос агрегации в специализированный BFF-слой или использование асинхронного GraphQL Federation.

3. Как реализовать распределенный Rate Limiting на API Gateway при наличии 10 параллельных инстансов шлюза?

Ответ: Локальные in-memory счетчики не подходят, так как балансировщик распределяет запросы между 10 инстансами шлюза, и клиент сможет превысить квоту в 10 раз. Решение: Использование Redis Token Bucket:

  1. Все инстансы шлюза обращаются к общему кластеру Redis.
  2. Проверка и списание токенов выполняется атомарно через Lua-скрипт (встроенный в Spring Cloud Gateway RedisRateLimiter), что исключает race conditions.
  3. При исчерпании лимита возвращается заголовок HTTP 429 Too Many Requests с заголовком Retry-After.

4. В чем опасность проверки цифровой подписи JWT-токена (RSA / ECDSA) на каждый запрос в шлюзе при 100 000 RPS?

Ответ: Проверка асимметричной криптографической подписи (RS256) является ресурсоемкой математической операцией для CPU. При 100 000 RPS шлюз может исчерпать все доступные ядра процессора исключительно на проверке подписей токенов. Оптимизация:

  • Кэширование валидированных токенов в локальном in-memory кэше (Caffeine) на время их жизни (TTL) по хэшу токена.
  • Использование эллиптических кривых (ES256 / EdDSA), которые требуют значительно меньше процессорного времени по сравнению с тяжелыми ключами RSA 2048/4096.

🎯 Шпаргалка для интервью

Главное:

  • API Gateway — централизованная входная дверь в микросервисную систему.
  • Базовые функции: Роутинг, TLS Termination, аутентификация (JWT/OAuth2), Rate Limiting, CORS, трассировка (Correlation-ID).
  • Стек: В Java-мире стандартом является реактивный Spring Cloud Gateway (WebFlux + Netty). Вне Java — Kong, Envoy, Nginx, Traefik.
  • Архитектурный принцип: «Тонкий и глупый шлюз» (Smart endpoints, dumb pipes). Никакой тяжелой бизнес-логики и работы с БД в шлюзе!
  • Для агрегации разнородных клиентских интерфейсов используется шаблон BFF (Backend for Frontend), а не перегрузка центрального Gateway.

Предохранители на шлюзе:

  1. Обязательный Rate Limiter (Redis Token Bucket).
  2. Обязательный Circuit Breaker (Resilience4j / Reactive CircuitBreaker) на каждый маршрут.
  3. Жесткие HTTP Connection/Read Timeouts на бэкенды.

Красные флаги (Чего говорить НЕЛЬЗЯ):

  • ❌ «В API Gateway мы пишем валидацию бизнес-правил и пишем аудит напрямую в базу данных» (Шлюз деградирует в монолит и становится SPOF).
  • ❌ «Spring Cloud Gateway работает на базе Tomcat сервлетов» (Он реактивный и работает исключительно на Netty/WebFlux).
  • ❌ «Для масштабирования шлюза достаточно одного инстанса» (Шлюз обязан работать в HA-кластере из нескольких реплик за аппаратным или облачным балансировщиком).

Связанные темы: