SOA (Service-Oriented Architecture)¶
SOA - стиль архитектуры, в котором система собирается из сетевых сервисов с явными контрактами. Каждый сервис отвечает за свою бизнес-функцию, а остальные обращаются к нему только через контракт.
Note
SOA - это подход к проектированию, а не конкретный стек. SOAP, WS-* и ESB - самая известная его реализация, но не определение. REST-сервисы, нарезанные по бизнес-функциям, - тоже SOA.
Термин ввели аналитики Gartner Yefim Natis и Roy Schulte (1996). SOA выросла из задачи интеграции: как связать между собой уже работающие системы на разных технологиях. Вопрос «как построить новое приложение из сервисов» появился позже.
Что считается сервисом¶
Сервис в SOA - всё, у чего есть сетевой контракт:
- веб-сервис
- обёртка над легаси-системой или мейнфреймом
- промышленный контроллер или датчик
- покупная система (CRM, ERP) со своим API
В микросервисах сервис почти всегда - отдельно деплоящееся приложение со своей БД. В SOA понятие шире.
Зачем нужна SOA¶
- Много команд. Они мешают друг другу в общем репозитории и общем релизном цикле. Самая частая причина.
- Независимые релизы. Изменение в одном сервисе выкатывается, не затрагивая остальные.
- Зоопарк систем. Легаси, покупные системы и новый код общаются через единые контракты. Исторически главная причина SOA в enterprise.
- Разная нагрузка на части. Поиск нагружен в сто раз сильнее биллинга, и масштабировать его нужно отдельно.
- Разные технологии. Одному сервису нужна Java, другому Python с ML-библиотеками.
- Изоляция отказов. Упали отчёты - приём заказов продолжает работать.
- Переиспользование. Один сервис аутентификации или один
CustomerServiceна всю компанию.
Когда SOA не нужна¶
Нужна только горизонтальная масштабируемость. Распределённость - свойство развёртывания, а не архитектуры. Монолит тоже масштабируется: N копий одного артефакта за балансировщиком. Java EE / Jakarta EE даёт распределённость на уровне сервера приложений: кластер WildFly решает задачу без переделки кода.
Note
С лекции: если ты не опытный архитектор, кластер на WildFly лучше самодельных микросервисов.
Одна небольшая команда. Закон Конвея: структура системы повторяет структуру общения в организации. Команде из пяти человек сервисы дадут все проблемы распределённой системы и ни одного преимущества.
Принципы¶
- Слабая связанность - сервисы минимально зависят друг от друга и обращаются друг к другу редко.
- Явный контракт - интерфейс сервиса описан формально (WSDL, OpenAPI), протоколы стандартные.
- Автономность - сервис сам выполняет свою функцию и владеет своими данными, никто не ходит к нему в базу напрямую. Если соседи упали, он продолжает работать хотя бы частично. Отсюда таймауты, circuit breaker и кеши.
- Stateless - сервис не хранит состояние между запросами, поэтому любой запрос может обработать любой экземпляр.
- Прозрачность размещения - потребителю не важно, где физически стоит сервис. Он обращается по имени (URI), а конкретный адрес (URL) находится через реестр.
- Нормализация - у каждой бизнес-функции один сервис-владелец, функции не дублируются.
Как резать на сервисы¶
Границы проводят по бизнес-подобластям - bounded context из DDD: у каждой своя модель и свой язык. Хорошая граница там, где связей между частями мало и они редкие.
Это аналитическая работа, и сделать её хорошо сложно. Ошибиться можно в обе стороны:
- Слишком крупные сервисы - по сути тот же монолит, только с сетью. Делить не было смысла.
- Слишком мелкие - сервисы постоянно вызывают друг друга. Сеть - самая медленная часть системы, и производительность падает.
Правильный размер - всегда компромисс между этими крайностями. Для некоторых систем хорошей нарезки нет вовсе.
Как устроено: поставщик, реестр, потребитель¶
Классическая модель SOA состоит из трёх ролей:
┌──────────────┐
publish │ Реестр │ find
┌─────────────▶│ (registry) │◀─────────────┐
│ └──────────────┘ │
┌─┴───────────┐ ┌───────┴─────┐
│ Поставщик │◀──────── bind ─────────│ Потребитель │
│ (provider) │ вызов по контракту │ (consumer) │
└─────────────┘ └─────────────┘
- Поставщик публикует описание своего сервиса в реестре (publish).
- Потребитель ищет в реестре сервис по нужным критериям (find).
- Потребитель получает контракт и адрес и вызывает сервис напрямую (bind).
В 2000-х реестр задумывали глобальным: стандарт UDDI описывал каталог, где компании публикуют свои сервисы и находят чужие. Это не прижилось, публичные реестры закрылись примерно к 2006 году.
Внутри компаний идея осталась:
- service discovery - Consul, etcd, Eureka, DNS и Endpoints в Kubernetes: найти адрес сервиса по имени
- каталоги контрактов - Schema Registry для Kafka, внутренние API-порталы, Backstage
Классическая SOA: ESB, SOAP, WS-*¶
Обычно различают два вида SOA:
- классическая (enterprise) - общая шина ESB, централизованная оркестрация, SOAP и стандарты WS-*
- лёгкая - сервисы общаются напрямую по HTTP/REST или через простой брокер сообщений
ESB (Enterprise Service Bus)¶
ESB - центральная шина интеграции. Сервисы не вызывают друг друга напрямую, все сообщения идут через шину.
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Billing │ │ CRM │ │ Inventory│ │ Legacy │
│ service │ │ service │ │ service │ │ mainframe│
└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
▼ ▼ ▼ ▼
════════════════════════════════════════════════════════
║ ESB (routing, transform, orchestration) ║
════════════════════════════════════════════════════════
▲ ▲ ▲
│ │ │
┌────┴─────┐ ┌────┴─────┐ ┌────┴─────┐
│ Orders │ │ Shipping │ │ Partner │
│ service │ │ service │ │ gateway │
└──────────┘ └──────────┘ └──────────┘
Что делает шина:
- routing - решает, куда доставить сообщение (а не отправитель)
- transformation - на лету переводит формат одного сервиса в формат другого
- protocol bridging - связывает сервисы на разных протоколах, например SOAP и JMS
- orchestration - собирает несколько вызовов в один бизнес-процесс, часто на BPEL
Вся логика интеграции живёт в шине, а сервисы на концах простые. Это называют smart pipes, dumb endpoints. В микросервисах наоборот: dumb pipes, smart endpoints.
Минусы ESB:
- Упала шина - встала вся система.
- Шина ограничивает производительность всех сервисов сразу.
- Любое изменение интеграции делает одна команда шины, остальные ждут в очереди. Независимых релизов, ради которых делили систему, больше нет.
- Логика размазана между сервисами и шиной, отлаживать тяжело.
- ESB-продукты дорогие и проприетарные (vendor lock-in).
Для небольшой системы ESB избыточна: сервисы спокойно общаются напрямую.
SOAP и WSDL¶
SOAP - протокол обмена XML-сообщениями. Сообщение - это конверт
(Envelope) со служебными заголовками (Header) и телом (Body). Обычно отправляется POST'ом на один URL поверх
HTTP, но может идти и через JMS.
WSDL - XML-контракт сервиса: какие есть операции, какие данные они принимают и возвращают, по какому адресу их вызывать. По WSDL генерируется код клиента и сервера.
WS-*¶
WS-* - набор стандартов поверх SOAP. Каждый добавляет свой блок в Header и
закрывает то, чего нет в самом SOAP:
- WS-Security - подпись и шифрование самого сообщения
- WS-Addressing - ID сообщения и адрес для ответа, асинхронный обмен
- WS-ReliableMessaging - гарантированная доставка
- WS-AtomicTransaction - распределённые транзакции (2PC)
Плюсы и минусы¶
Плюсы - это причины из раздела «Зачем нужна SOA», когда они действительно сработали. Сверх них:
- Единые контракты - разнородные системы интегрируются по одним правилам.
- Централизованные стандарты безопасности, логирования и мониторинга. В большой регулируемой организации это скорее плюс.
- Нарезка по бизнесу как явный принцип - задолго до того, как DDD стал мейнстримом.
Минусы:
- Сложнее архитектура - сеть, контракты, отдельное развёртывание каждого сервиса.
- Нет общих транзакций - ACID между сервисами невозможен, нужны Saga, Outbox и eventual consistency.
- Сложнее отладка - стектрейс через сеть не проходит, нужен распределённый трейсинг.
- Сложнее тестировать взаимодействие сервисов.
- Больше эксплуатации - service discovery, централизованные логи, CI/CD на каждый сервис.
- Версионирование контрактов - сигнатуру нельзя просто поменять, у сервиса есть живые потребители.
- Никто не видит систему целиком - нужны документация и ADR.
- Общая БД (частая в SOA) - скрытая связанность: изменил схему ради одного сервиса, сломал три других.
- Тяжёлый стек - SOAP и WS-* многословны, инструменты громоздкие, порог входа высокий.
- Минусы ESB - см. ESB.
SOA и микросервисы¶
| Ось | SOA | Микросервисы |
|---|---|---|
| Размер сервиса | крупные, вокруг бизнес-домена | мелкие, вокруг одной capability |
| Коммуникация | через ESB, smart pipes | dumb pipes, smart endpoints |
| Данные | часто общая БД / общая модель | у каждого сервиса своя БД |
| Координация | централизованная оркестрация в ESB | хореография или лёгкая оркестрация |
| Управление | governance сверху, единые стандарты | автономия команд |
| Протоколы | SOAP / WS-*, тяжёлые контракты | HTTP/REST, gRPC, лёгкие контракты |
Микросервисы можно считать развитием SOA: та же идея сервисов вокруг бизнес-функций, но без центральной шины и общей базы (см. Микросервисы).
TODO¶
- Компенсация распределённости: таймауты, retry с backoff, идемпотентность, circuit breaker.
- Согласованность: почему 2PC плохо работает между сервисами и что вместо него.
- Strangler Fig как практический путь от монолита к сервисам.
- Fallacies of Distributed Computing - хорошо подытоживают всё про сеть.