Эта статья представляет собой отправную точку для понимания общих шаблонов архитектуры Microservice на примере проверенного приложения, созданного с использованием Spring Boot, Spring Cloud и Docker.
Код доступен на Github , а изображения доступны на Docker Hub. Вы можете запустить всю систему с помощью одной команды.
В качестве основы для этой системы я выбрал старый проект, бэкэнд которого раньше был монолитным. Приложение позволяет управлять личными финансами, организовывать доходы и расходы, управлять сбережениями, анализировать статистику и создавать простые прогнозы.
Функциональные услуги
Применение монолита было разложено на три основных микросервиса. Все они являются независимо развертываемыми приложениями, организованными вокруг определенных бизнес-возможностей.
Обслуживание учетной записи
Содержит общую логику пользовательского ввода и проверки: статьи доходов / расходов, сбережения и настройки учетной записи.
метод | Дорожка | Описание | Пользователь аутентифицирован | Доступно из пользовательского интерфейса |
---|---|---|---|---|
ПОЛУЧИТЬ | / счета / {счета} | Получить указанные данные аккаунта | ||
ПОЛУЧИТЬ | / Счета / ток | Получить данные текущего аккаунта | × | × |
ПОЛУЧИТЬ | / Счета / демо | Получить данные демо-счета (предварительно заполненные статьи доходов / расходов и т. Д.) | × | |
ПОЛОЖИТЬ | / Счета / ток | Сохранить данные текущего аккаунта | × | × |
ПОСЛЕ | /Счета/ | Зарегистрировать новый аккаунт | × |
Служба статистики
Выполняет расчеты по основным параметрам статистики и фиксирует временные ряды для каждой учетной записи. Точка данных содержит значения, нормализованные к базовой валюте и периоду времени. Эти данные могут быть использованы для отслеживания динамики денежных потоков за время существования учетной записи.
метод | Дорожка | Описание | Пользователь аутентифицирован | Доступно из пользовательского интерфейса |
---|---|---|---|---|
ПОЛУЧИТЬ | / Статистика / {счет} | Получить указанную статистику аккаунта | ||
ПОЛУЧИТЬ | / Статистика / ток | Получить текущую статистику аккаунта | × | × |
ПОЛУЧИТЬ | / Статистика / демо | Получить статистику демо-счета | × | |
ПОЛОЖИТЬ | / Статистика / {счет} | Создание или обновление точки данных временного ряда для указанной учетной записи |
Служба уведомлений
Хранит контактную информацию пользователя и настройки уведомлений (например, напоминание и частоту резервного копирования). Запланированный работник собирает необходимую информацию из других служб и отправляет сообщения электронной почты подписанным клиентам.
метод | Дорожка | Описание | Пользователь аутентифицирован | Доступно из пользовательского интерфейса |
---|---|---|---|---|
ПОЛУЧИТЬ | / Уведомление / Настройки / ток | Получить текущие настройки уведомлений учетной записи | × | × |
ПОЛОЖИТЬ | / Уведомление / Настройки / ток | Сохранить настройки уведомлений текущей учетной записи | × | × |
Заметки
- У каждого микросервиса есть своя собственная база данных, поэтому невозможно обойти API и получить прямой доступ к данным персистентности.
- Для этого проекта я использовал MongoDB в качестве основной базы данных для каждого сервиса. Также может иметь смысл иметь постоянную архитектуру полиглота (чтобы выбрать тип базы данных, который лучше всего подходит для требований сервиса).
- Связь между сервисами довольно упрощена: микросервисы общаются, используя только синхронный REST API. Обычной практикой в реальных системах является использование комбинации стилей взаимодействия. Например, выполнить синхронный запрос GET для извлечения данных и использовать асинхронный подход через посредник сообщений для операций создания / обновления, чтобы разделить службы и буферизировать сообщения. Тем не менее, это приводит нас в возможной последовательности мира.
Инфраструктурные услуги
В распределенных системах существует множество общих шаблонов, которые могут помочь нам заставить описанные основные сервисы работать. Spring Cloud предоставляет мощные инструменты, которые улучшают поведение приложений Spring Boot для реализации этих шаблонов. Я кратко расскажу о них.
Конфиг Сервис
Spring Cloud Config — это горизонтально масштабируемая служба централизованной конфигурации для распределенных систем. Он использует сменный слой хранилища, который в настоящее время поддерживает локальное хранилище, Git и Subversion.
В этом проекте я использую native profile
, который просто загружает файлы конфигурации из локального пути к классам. Вы можете увидеть shared
каталог в ресурсах сервиса Config . Теперь, когда служба уведомлений запрашивает свою конфигурацию, служба Config отвечает с помощью shared/notification-service.yml
и shared/application.yml
(которая используется всеми клиентскими приложениями).
Использование на стороне клиента
Просто создайте приложение Spring Boot с spring-cloud-starter-config
зависимостями, остальное сделает автоконфигурация.
Теперь вам не нужны встроенные свойства в вашем приложении. Просто укажите bootstrap.yml
имя приложения и URL-адрес службы конфигурации:
spring:
application:
name: notification-service
cloud:
config:
uri: http://config:8888
fail-fast: true
С помощью Spring Cloud Config вы можете динамически изменять конфигурацию приложения
Например, компонент EmailService был аннотирован @RefreshScope
. Это означает, что вы можете изменить текст сообщения электронной почты и строки темы, не перестраивая и не перезапуская приложение службы уведомлений.
Сначала измените необходимые свойства на сервере конфигурации. Затем выполните запрос на обновление службы уведомлений: curl -H "Authorization: Bearer #token#" -XPOST http://127.0.0.1:8000/notifications/refresh
Вы также можете использовать webhooks для автоматизации этого процесса .
Заметки
- Есть некоторые ограничения для динамического обновления.
@RefreshScope
не работает с@Configuration
классами и не может влиять на@Scheduled
методы. fail-fast
свойство означает, что приложение Spring Boot сразу не сможет запуститься, если не сможет подключиться к службе конфигурации. Это очень полезно, когда вы запускаете все приложения вместе .- Ниже приведены важные замечания по безопасности.
Аут Сервис
Ответственность за авторизацию полностью распределяется на отдельный сервер, который предоставляет токены OAuth2 для служб ресурсов бэкэнда. Auth Server используется для авторизации пользователя, а также для безопасной межмашинной связи внутри периметра.
В этом проекте я использую Password credentials
в качестве типа предоставления для авторизации пользователя (поскольку он используется только в пользовательском интерфейсе приложения) и Client Credentials
в качестве типа предоставления для авторизации на микросервисах.
Spring Cloud Security предоставляет удобные аннотации и автоконфигурации, чтобы сделать это действительно простым для реализации как на стороне сервера, так и на стороне клиента. Вы можете узнать больше об этом в документации и проверить детали конфигурации в коде Auth Server .
Со стороны клиента все работает точно так же, как и при традиционной авторизации на основе сеансов. Вы можете извлекать Principal
объекты из запроса, проверять роли пользователей и другие вещи с помощью контроля доступа на основе выражений и @PreAuthorize
аннотаций.
Каждый клиент в PiggyMetrics (служба учетных записей, служба статистики, служба уведомлений и браузер) имеет область действия: server
для внутренних служб и ui
— для браузера. Таким образом, мы также можем защитить контроллеры от внешнего доступа, например:
@PreAuthorize("#oauth2.hasScope('server')")
@RequestMapping(value = "accounts/{name}", method = RequestMethod.GET)
public List<DataPoint> getStatisticsByAccountName(@PathVariable String name) {
return statisticsService.findByAccountName(name);
}
API-шлюз
Как видите, есть три основных сервиса, которые предоставляют клиенту внешние API. В реальной системе это число может расти очень быстро, а также сложность всей системы. На самом деле, сотни служб могут быть вовлечены в создание одной сложной веб-страницы.
Теоретически, клиент может делать запросы к каждому из микросервисов напрямую. Но очевидно, что у этой опции есть проблемы и ограничения, такие как необходимость знать все адреса конечных точек, выполнять http-запрос для каждого блока информации отдельно, объединять результаты на стороне клиента. Другая проблема — это не-дружественные протоколы, которые могут быть использованы на сервере.
Обычно гораздо лучший подход — использовать API-шлюз. Это единственная точка входа в систему, используемая для обработки запросов путем их перенаправления в соответствующий бэкэнд-сервис или вызова нескольких бэкэнд-сервисов и агрегирования результатов . Кроме того, его можно использовать для аутентификации, аналитики, стресс-тестирования и канареечного тестирования, миграции сервисов, обработки статических ответов, активного управления трафиком.
Netflix с открытым исходным кодом предоставляет такой пограничный сервис , и теперь с помощью Spring Cloud мы можем включить его с одной @EnableZuulProxy
аннотацией. В этом проекте я использую Zuul для хранения статического контента (приложение пользовательского интерфейса) и для маршрутизации запросов к соответствующим микросервисам. Вот простая конфигурация маршрутизации на основе префиксов для службы уведомлений:
zuul:
routes:
notification-service:
path: /notifications/**
serviceId: notification-service
stripPrefix: false
Это означает, что все запросы, начиная с, /notifications
будут направлены в службу уведомлений. Как вы видите, нет жестко закодированного адреса. Zuul использует механизм обнаружения службы для обнаружения экземпляров службы уведомлений, а также прерыватель цепи и балансировщик нагрузки , описанные ниже.
Сервис Discovery
Другим широко известным архитектурным шаблоном является обнаружение службы. Это позволяет автоматически определять сетевые расположения для экземпляров службы, которые могут иметь динамически назначенные адреса из-за автоматического масштабирования, сбоев и обновлений.
Ключевой частью службы обнаружения является реестр. Я использовал Netflix Eureka для этого проекта. Eureka является хорошим примером шаблона обнаружения на стороне клиента, когда клиент отвечает за определение местоположения доступных экземпляров службы (с использованием сервера реестра) и запросов балансировки нагрузки между ними.
С помощью Spring Boot вы можете легко создать Eureka Registry с spring-cloud-starter-eureka-server
зависимостями, @EnableEurekaServer
аннотациями и простыми свойствами конфигурации.
Поддержка клиентов включена с @EnableDiscoveryClient
аннотацией и bootstrap.yml
именем приложения:
spring:
application:
name: notification-service
Теперь при запуске приложения оно регистрируется на сервере Eureka и предоставляет метаданные, такие как хост и порт, URL-адрес индикатора работоспособности, домашняя страница и т. Д. Eureka получает сообщения пульса от каждого экземпляра, принадлежащего службе. Если сердцебиение не выполняется в течение настраиваемого расписания, экземпляр будет удален из реестра.
Кроме того, Eureka предоставляет простой интерфейс, где вы можете отслеживать запущенные сервисы и количество доступных экземпляров: http://localhost:8761
Балансировщик нагрузки, автоматический выключатель и клиент Http
Netflix OSS предоставляет еще один отличный набор инструментов.
лента
Лента — это балансировщик нагрузки на стороне клиента, который дает вам большой контроль над поведением клиентов HTTP и TCP. По сравнению с традиционным балансировщиком нагрузки нет необходимости в дополнительном прыжке для каждого беспроводного вызова — вы можете напрямую связаться с нужной службой.
Изначально он интегрируется с Spring Cloud и Service Discovery. Eureka Client предоставляет динамический список доступных серверов, чтобы лента могла балансировать между ними.
Hystrix
Hystrix — это реализация шаблона прерывателя цепи , который дает контроль над задержкой и отказом от зависимостей, доступных по сети. Основная идея заключается в том, чтобы остановить каскадные сбои в распределенной среде с большим количеством микросервисов. Это помогает быстро выходить из строя и восстанавливаться как можно быстрее — важные аспекты отказоустойчивых систем, которые самовосстанавливаются.
Помимо управления прерывателем цепи, с Hystrix вы можете добавить запасной метод, который будет вызываться для получения значения по умолчанию в случае сбоя основной команды.
Кроме того, Hystrix генерирует метрики по результатам выполнения и задержке для каждой команды, которые мы можем использовать для мониторинга поведения системы .
Feign
Feign — декларативный HTTP-клиент, который легко интегрируется с Ribbon и Hystrix. Фактически, с одной spring-cloud-starter-feign
зависимостью и @EnableFeignClients
аннотацией у вас есть полный набор балансировщика нагрузки, автоматического выключателя и HTTP-клиента с разумной готовой конфигурацией по умолчанию.
Вот пример из службы учетных записей:
@FeignClient(name = "statistics-service")
public interface StatisticsServiceClient {
@RequestMapping(method = RequestMethod.PUT, value = "/statistics/{accountName}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
void updateStatistics(@PathVariable("accountName") String accountName, Account account);
}
- Все, что вам нужно, это просто интерфейс
- Вы можете разделить
@RequestMapping
часть между Spring MVC контроллером и Feign методами - В приведенном выше примере указан только нужный идентификатор службы —
statistics-service
благодаря автообнаружению через Eureka (но, очевидно, вы можете получить доступ к любому ресурсу с определенным URL)
Панель мониторинга монитора
В этой конфигурации проекта каждый микросервис с Hystrix на борту передает метрики в Turbine через Spring Cloud Bus (с брокером AMQP). Проект Monitoring — это всего лишь небольшое загрузочное приложение Spring с Turbine и Hystrix Dashboard .
Давайте посмотрим, как работает наша система под нагрузкой: служба учетных записей вызывает службу статистики, и она реагирует с различной задержкой имитации. Порог ожидания ответа установлен на 1 секунду.
Анализ журнала
Централизованное ведение журнала может быть очень полезным при попытке выявления проблем в распределенной среде. Стек Elasticsearch, Logstash и Kibana позволяет с легкостью искать и анализировать ваши журналы, данные об использовании и сетевой активности. Готовая конфигурация Docker описана в моем другом проекте .
Безопасность
Расширенная конфигурация безопасности выходит за рамки этого проверенного проекта. Для более реалистичного моделирования реальной системы рассмотрите возможность использования хранилища ключей https и JCE для шифрования паролей микросервисов и содержимого свойств сервера конфигурации ( подробности см. В документации ).
Автоматизация инфраструктуры
Развертывание микросервисов с их взаимозависимостью является гораздо более сложным процессом, чем развертывание монолитного приложения. Важно иметь полностью автоматизированную инфраструктуру. С помощью подхода непрерывной доставки мы можем добиться следующих преимуществ:
- Возможность выпуска программного обеспечения в любое время.
- Любая сборка может оказаться релизом.
- Создавайте артефакты один раз, развертывайте по мере необходимости.
Вот простой рабочий процесс Continuous Delivery, реализованный в этом проекте:
В этой конфигурации Travis CI создает помеченные изображения для каждого успешного нажатия Git. Таким latest
образом, в Docker Hub всегда есть образ для каждого микросервиса, а старые изображения помечаются хэшем Git commit. Легко развернуть любой из них и быстро откатиться, если это необходимо.
Как запустить все вещи?
Это действительно легко, и я предлагаю вам попробовать. Помните, что вы собираетесь запустить 8 приложений Spring Boot, 4 экземпляра MongoDB и RabbitMq. Убедитесь, что 4 Gb
на вашем компьютере есть оперативная память. Вы всегда можете запустить только жизненно важные службы, хотя Gateway, Registry, Config, Auth Service и Account Service.
Прежде чем ты начнешь
- Установите Docker и Docker Compose.
- Переменные среды Экспорт:
CONFIG_SERVICE_PASSWORD
,NOTIFICATION_SERVICE_PASSWORD
,STATISTICS_SERVICE_PASSWORD
,ACCOUNT_SERVICE_PASSWORD
,MONGODB_PASSWORD
Режим производства
В этом режиме все последние изображения будут извлечены из Docker Hub. Просто скопируйте docker-compose.yml
и нажмите docker-compose up -d
.
Режим разработки
Если вы хотите создавать изображения самостоятельно (например, с некоторыми изменениями в коде), вам необходимо клонировать все хранилище и создавать артефакты с помощью Maven. Затем беги docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
docker-compose.dev.yml
наследует docker-compose.yml
с дополнительной возможностью создавать образы локально и выставлять все порты контейнеров для удобной разработки.
Важные конечные точки
- localhost: 80 — шлюз
- localhost: 8761 — Eureka Dashboard
- localhost: 9000 — Панель управления Hystrix
- localhost: 8989 — Поток турбины (источник для Hystrix Dashboard)
- localhost: 15672 — управление RabbitMq
Заметки
Для запуска всех приложений Spring Boot требуется уже запущенный Config Server . Но мы можем запустить все контейнеры одновременно из-за fail-fast
свойства Spring Boot и restart: always
опции docker-compose. Это означает, что все зависимые контейнеры будут пытаться перезапускаться до тех пор, пока Config Server не будет запущен и запущен.
Кроме того, для механизма обнаружения служб требуется некоторое время после запуска всех приложений. Любая служба недоступна для обнаружения клиентами до тех пор, пока экземпляр, сервер Eureka и клиент не будут иметь одинаковые метаданные в своем локальном кэше, поэтому это может занять 3 такта. Период прослушивания по умолчанию составляет 30 секунд.