Spring: Cloud
Основные выгоды от использования микросервисов:
- повышают изолированность;
- повышают общую стабильность;
- простота внедрения изменений и тестирования системы в целом;
- улучшенное объединение для работы над 1 общим делом;
- повышенная независимость различных частиц системы от других;
- удобство в разработке, рефакторинге и доставке измнений;
- четкая специализация - каждый микросервис отвечает за небольшой участок системы;
- каждый микросервис гибкий, небольшой, динамичный и ограниченный в ответственности;
- условная безопасность от отказа в работе всей системы;
- необязательность использования единого стэка разработки, библиотек или языка прогрммирования;
- простая адаптация для новых членов команды;
- простота в увеличении и снижении потребляемых системой ресурсов.
У них так же присутствуют и минусы:
- Опасность при нарушении в работе какого-нибудь “центрального” микросервиса в системе. Система в целом не будет считаться недоступной, но бизнес-процессы будут ограничены или заблокированы;
- Требуется контракт между микросервисами о формате обмена данными и жесткое следование ему;
- Возможно сложное координирование обновления всех микросервисов, если выполняется какая-либо массовая критическая миграция;
- Сложность в обработке распределенных транзакций (distributed transactions);
- В случае если присутствует несколько (версий) протоколов обмена данными - все их придется поддерживать, тестировать и мониторить.
Микросервисы и источники данных
Существует несколько стратегий использования источников данных (в частности, БД):
- частные-объекты. Каждый микросервис имеет (эксклюзивный) доступ к ограниченному набору таблиц (и прочих объектов). Реализовать можно, например, посредством авторизации в источнике данных;
- 1-схема-1-сервис. К 1 схеме в рамках 1 источника (если такое разделение, конечно, преусмотренно в источнике данных) доступ присутствует только у 1 сервиса;
- 1-источник-1-сервис. К 1 источнику данных доступ ограничен только 1му сервису.
Spring Cloud
“Из коробки” Spring Cloud предлагает сразу несколько компонентов, которые упростят работу и оркестрирование микросервисами:
- Spring Could Config - управление конфигурациями;
- Eureka - компонент межсервисного взаимодействия, который обеспечивает балансировку нагрузки, отказоустойчивость, поиск других компонентов системы и т.д.;
- Netflix’s Hysterix - компонент, который ведет учет неактивных сервисов и перераспределения трафика;
- Zuul - распределяет трафик;
- micro-proxy - межсетевые прокси сервисы со стороны клиентов;
- control bus - компонент, который выполняет мониторинг и управление компонентами, исползуя технические сообщения;
- Spring Vault - система управления безопасностью на основе токенов;
- global locks - глобальные замки, накладываемые на ресурсы;
- leadership election - компонент, служащий процессом-дирижёром для нескольких подчиненных как-то связанных процессов, выполняемых параллельно (или последовательно) на нескольких компонентах
- Spring Cloud Bus - брокер сообщений;
- load balancing на стороне клиента - распреление нагрузки по нескольким узлам.
Eureka
Данные компонент крайне важен, поскольку он позволяет объединять несколько компонентов в одну общую систему путем поиска их в рамках сети.
Для подключения данного компонента следует добавить набор библиотек, необходимых для него. Все они организованы с несколько зависимостей:
org.springframework.cloud:spring-cloud-starter-netflix-eureka-serverorg.springframework.cloud:spring-cloud-starter-netflix-eureka-client
Для включения компонента поиска сервисов необходимо в класс, инициализирующий Spring добавить соответствующую аннотацию:
1 |
|
В конфигурации Spring так же следует добавить конфигурацию относительно расположения Eureka сервера:
1 | eureka.instance.hostname=localhost |
Данные конфигурации выполняются для настройки, по сути, пустого Spring приложения, который выступает исключительно в качестве сервера Eureka. После запуска приложения, Spring самостоятельно откроет порт 3000, в котором будет представлена информация по Eureka серверу по пути /eureka.
Добавление сервисов в Eureka
Для идентификации себя, сервис должен уведомить Eureka о своем существовании как компонента - тогда он станет доступеным для других компонентов. Для сбора информации от сервисов (и для распределения нагрузки между ними), по ум. используется библиотека Ribbon.
Для регистрации микросервиса как компонента в рамках Eureka, потребуется опредлить его как клиента:
1 |
|
При этом, в Spring конфигурациях необходимо определить настройки клиента, а так же передать имя для приложения (де-факто, микросервиса):
1 | spring.application.name=my-service-app |
В данном случае приложение подключается уже как клиент для Eureka. Этому свидетельствует аннотация @EnableEurekaClient. Конфигурация defaultZone - определяет адрес до севера Eureka. В отличае от приложения с сервером Eureka, в клиенте указывается параметр registerWithEureka со значением true, что говорит о том что данный компонент стоит зарегистрировать. Параметр fetchRegistry со значением false говорит о том, что для данного компонента нет необходимости синхронизировать наличие других компонентов (значение true применяется если компонент будет взаимодействовать с другими компонентами как их клиент).
Параметр leaseRenewalIntervalInSeconds определяет частоту отправки уведомлений о своем состоянии на сервер Eureka. Де-факто отправляется просто уведомление что компонент активен. По умолчанию равен 30 секундам.
После запуска приложения, приложение-клиент запросит всю доступную информацию по имеющимся компонентам и сохранит их в локально в RAM.
На Eureka сервере же, можно обратить внимание как компонент попадет в список зарегистрированных. Их полный список доступен /eureka/apps. После регистрации в Eureka каждому компоненту выдается уникальный идентификатор. Идентификатор имеет следующий шаблон:
1 | ${ip_address}:${spring.application.name:${server.port}} |
Шаблон идентификатора можно поменять:
1 | eureka.instance.metadataMap.instanceId=${spring.application.name}:${spring.application.name}:${my.company.props.env}:${server.port} |
Каждый идентификатор уникален и в случае если он совпадет с уже существующим, Eureka будет рассматривать данное событие как перезапуск компонента.
Коммуникации между компонентами
Для коммуникации между сервисами в стандартном пакете предусмотрен следующие шаги реализации:
- Регистрация бина с типом
RestTemplateи аннотацией@LoadBalanced.; - Реализовать
@Controller, который будет принимать запрос клиента и ретранслировать его (целиком, или видоизмененно) на один из компонентов Eureka; - В момент запроса данных у одного из компонентов Eureka, должен быть использован
RestTemplate, которому в качестве URI следует передавать имя микросервиса и название метода, к которому следует обратиться; - Реализовать
LoadBalancerClientи переопределить ему методreconstructURIтаком образом чтобы он принимая URI в составе которого будет компонент Eureka, перенаправлял запрос на компонент, а не выполнял поиск узла где-то вне Eureka;
1 | public class MyRibbonBalancer implements LoadBalancerClient { |
- При обращении клиента к сервису, необходимо ретранслировать его к компоненту Eureka используя
RestTemplate:
1 | MyObject[] objects = restTemplate.getForObject("http://my-eureka-service/some-service", MyObject[].class); |
или
1 | User[] users = restTemplate.getForObject("http://my-users-service/find-all", User[].class); |
Выполнение запроса/ответа к компонентам Eureka следует воспринимать как “обычные” обращения к веб-сервисам. Вместо RestTemplate можно использовать любой другой клиент - ему необходимо будет так же изменить стандартное поведение в случае обращения к Eureka компоненту.