Инженерия ПО
Микросервисы: паттерны
Цели урока
- Знать каноничные паттерны микросервисов: Circuit Breaker, Saga, API Gateway, BFF, Service Mesh
- Понимать, когда нужен Saga вместо распределённой транзакции с 2PC
- Применять Circuit Breaker для защиты от каскадных отказов между сервисами
- Различать orchestration и choreography для Saga и видеть trade-off
- Использовать Outbox Pattern для гарантии 'один раз' при публикации событий
Предварительные знания
- Понимание чем микросервис отличается от монолита (предыдущий урок se-21)
- Знание REST/gRPC и брокеров сообщений (Kafka, RabbitMQ) на базовом уровне
- Опыт работы с любым облаком или контейнерами
Netflix 2012 год: переход с монолита на микросервисы. Сегодня 700+ микросервисов, 2+ миллиарда запросов в день через API Gateway, Chaos Monkey случайно убивает сервисы в production и система продолжает работать. Это стало возможным благодаря паттернам: Circuit Breaker предотвращает каскадные отказы, Saga обеспечивает distributed transactions, Service Mesh делает mTLS и observability прозрачными.
- **Netflix Hystrix**: первая широко используемая реализация Circuit Breaker в микросервисах, разработана для предотвращения каскадных отказов в 700+ сервисной экосистеме
- **Uber Eats Saga**: оформление заказа проходит через Payment, Restaurant, Driver services с compensating actions при отказе любого звена - Saga orchestration
- **Lyft Envoy**: разработали sidecar proxy для своей микросервисной архитектуры, который стал основой Istio Service Mesh - используется в тысячах production систем
API Gateway
API Gateway - единственная точка входа для клиентов в экосистему микросервисов. Принимает запросы от frontend/mobile, маршрутизирует к нужным сервисам, агрегирует ответы. Netflix API Gateway обрабатывает более 2 миллиардов запросов в день, агрегируя данные из 700+ микросервисов за один запрос клиента. Без Gateway клиент должен знать адреса всех сервисов и делать 10+ запросов для одного экрана.
Ответственности Gateway: аутентификация (JWT валидация один раз, не в каждом сервисе), rate limiting, SSL termination, request routing, response aggregation (Backend for Frontend паттерн - разные Gateway для mobile и web с разным агрегированием), circuit breaking, request/response трансформация. Риск: Gateway становится bottleneck и single point of failure. Решение: horizontal scaling и строгое ограничение бизнес-логики в Gateway.
Зачем JWT аутентификацию лучше выполнять в API Gateway, а не в каждом микросервисе отдельно?
Saga Pattern
Saga - паттерн для управления распределёнными транзакциями без 2PC (Two-Phase Commit). В микросервисах нет единой базы данных - каждый сервис со своей. Атомарная транзакция через несколько сервисов невозможна в классическом смысле. Saga решает это через последовательность локальных транзакций с compensating actions при неудаче.
Два вида Saga: Choreography (каждый сервис слушает события и публикует следующие - децентрализованно, сложно отследить общий flow) и Orchestration (центральный оркестратор знает весь flow и направляет сервисы командами - проще понять, single point). Uber Eats использует Saga для оформления заказа: Payment -> Restaurant -> Delivery - каждый шаг может откатиться с compensating action.
В Saga при оформлении заказа: Payment прошёл, Inventory.reserve() упал с ошибкой. Какой compensating action нужен?
Circuit Breaker
Circuit Breaker - паттерн предотвращения каскадных отказов. Называется по аналогии с электрическим автоматическим выключателем. Когда зависимый сервис начинает отвечать медленно или с ошибками - Circuit Breaker 'размыкает цепь': следующие запросы немедленно получают ошибку (fail fast) вместо ожидания timeout. Это предотвращает исчерпание thread pool и задержку в caller сервисе.
Три состояния: Closed (нормальная работа, запросы проходят), Open (цепь разомкнута, запросы fail fast - определяется порогом ошибок), Half-Open (пробный запрос - если успешен, переход в Closed; если неудачен - обратно в Open). Netflix Hystrix был первой popular реализацией (сейчас maintenance mode). Resilience4j для Java, Polly для .NET. Параметры: порог ошибок (50%), timeout для полуоткрытого состояния (10 сек).
Payment Service отвечает за 5 секунд вместо обычных 100ms. Order Service ждёт каждого запроса. Что происходит без Circuit Breaker?
Service Mesh
Service Mesh - инфраструктурный слой для управления коммуникацией между микросервисами. Реализуется через sidecar proxy (Envoy) рядом с каждым сервисом - перехватывает весь трафик. Приложение не знает о mesh: оно обращается к localhost, sidecar обрабатывает routing, retry, circuit breaking, mTLS, observability. Istio и Linkerd - самые распространённые реализации.
Service Mesh решает cross-cutting concerns без изменения кода сервисов: mutual TLS между всеми сервисами (zero trust networking), automatic retry с exponential backoff, circuit breaking на уровне инфраструктуры, distributed tracing (каждый запрос получает trace ID), traffic splitting для canary deployments (10% трафика в новую версию через routing rules). Lyft разработал Envoy именно для этих задач в своей микросервисной архитектуре.
Service Mesh решает все проблемы микросервисов - после его внедрения сервисы не нужно беспокоиться о network failures
Service Mesh обрабатывает network-level concerns (retry, circuit breaking, mTLS), но бизнес-логика обработки отказов (fallback данные, graceful degradation) остаётся в коде сервисов
Mesh не знает что вернуть пользователю когда Payment Service недоступен. Показать кешированный баланс? Заблокировать покупку? Это бизнес-решение, не инфраструктурное
Circuit Breaker уже реализован в коде Order Service через Resilience4j. Нужно ли добавлять circuit breaking ещё и в Service Mesh (Istio)?
Связанные темы
Паттерны микросервисов решают конкретные проблемы distributed systems:
- Event Sourcing и CQRS — Saga Choreography использует domain events для координации между сервисами без центрального оркестратора
- Монолит vs Микросервисы — Паттерны API Gateway и Circuit Breaker нужны именно из-за распределённости - в монолите эти проблемы не возникают
Вопросы для размышления
- Saga Choreography vs Orchestration: какие trade-offs определяют выбор для конкретного use case?
- Circuit Breaker с fallback к кешированным данным: когда устаревшие данные лучше чем ошибка, а когда нет?
- Service Mesh добавляет sidecar к каждому поду - это latency overhead и complexity. Когда это оправдано?