Статьи

Микросервисы с Spring Boot, Axon CQRS / ES и Docker

В последние год-два темпы изменений в архитектуре программного обеспечения быстро продвинулись, и новые подходы, такие как DevOps и Microservices, стали актуальными темами в одночасье.

В этой статье я хочу познакомить вас с проектом, над которым я работаю, который сочетает в себе два выдающихся архитектурных достижения последних нескольких лет: микросервисы и шаблон разделения ответственности за команды и запросы (или для краткости CQRS).

Образец микросервиса, который мы здесь обсуждаем, вращается вокруг вымышленного приложения с основными данными «Продукт», подобного тому, которое вы найдете в большинстве розничных или производственных компаний. Продукты можно добавлять, хранить, искать и получать с помощью простого API RESTful.

Исходный код приложения открыт и доступен на Github . Если вы загрузите его, у вас будет приложение, демонстрирующее, как реализовать несколько функций, необходимых для «облачной» Java, включая:

  • Микросервисы, использующие Java и Spring Boot ;
  • Разделение ответственности команд и запросов (CQRS) и источников событий (ES) с использованием Axon Framework v2 , MongoDB и RabbitMQ ;
  • Сборка, доставка и запуск в любом месте с использованием контейнеров Docker ;
  • Централизованная настройка и регистрация сервиса с использованием Spring Cloud ;
  • плюс документация по API с использованием Swagger и SpringFox .

В этой первой части я собираюсь использовать готовые контейнеры Docker, чтобы показать вам, как заставить пример кода работать с минимальными усилиями. Нет необходимости писать какой-либо код, вы можете просто попробовать компоненты самостоятельно, а потом взглянуть на код позже. В следующих статьях я расскажу подробнее, как можно было построить систему по частям.

Если вы просто хотите погрузиться прямо в код, вы можете найти все это на Github по адресу https://github.com/benwilcock/microservice-sampler.

Как это работает

Приложение построено с использованием архитектурного шаблона CQRS. В CQRS команды типа `ADD` физически отделены от запросов типа` VIEW (где id = 1) `. В этом примере кодовая база домена буквально разделена на два отдельных компонента — микросервис на стороне команды и микросервис на стороне запроса .

Как и большинство других 12-факторных приложений , эти микросервисы несут единую ответственность; показать свои собственные хранилища данных; и могут быть развернуты и масштабированы независимо друг от друга. Это CQRS и микросервисы в их наиболее буквальном толковании. Ни CQRS, ни микросервисы не должны быть реализованы таким образом, но для целей этой демонстрации я решил создать очень четкое разделение задач чтения и записи.

Логическая архитектура выглядит так:

CQRS-архитектура-01

Микросервисы как на стороне команды, так и на стороне запроса были разработаны с использованием среды Spring Boot для Java. Все взаимодействие между командным и запросным микросервисами является чисто «управляемым событиями». События передаются между компонентами микросервиса с помощью обмена сообщениями RabbitMQ. Обмен сообщениями обеспечивает масштабируемое средство передачи событий между процессами, микросервисами, устаревшими системами и другими сторонами в слабосвязанной форме.

Обратите внимание, что ни один из сервисов не делит свою базу данных с другим. Это важно из-за высокой степени автономности, которую он предоставляет каждой услуге, что, в свою очередь, помогает масштабировать отдельные службы независимо от других в системе. Чтобы узнать больше об архитектуре CQRS, посмотрите мой Slideshare о микросервисах CQRS, из которого взят слайд, приведенный выше.

Подробнее о командной стороне микросервиса

Команды — это « действия, которые меняют состояние ». Микросервис командной строки содержит всю логику домена и бизнес-правила. Команды используются для добавления новых продуктов или изменения их состояния. Выполнение этих команд в конкретном продукте приводит к генерации «событий», которые сохраняются платформой Axon в MongoDB и распространяются на другие процессы (столько процессов, сколько вам нужно) через обмен сообщениями RabbitMQ.

В источнике событий события являются единственной записью состояния системы. Они используются системой для описания и пересмотра текущего состояния любого объекта по требованию (путем воспроизведения его прошлых событий по одному, пока все предыдущие события не будут повторно применены). Это звучит медленно, но на самом деле, потому что события просты, они действительно быстрые и могут быть настроены в дальнейшем с помощью накопительных пакетов, называемых «снимками».

В доменно-управляемом дизайне (DDD) объект часто называют «агрегатом» или «агрегатом».

Подробнее о микросервисе на стороне запроса

Микросервис на стороне запроса действует как прослушиватель событий и представление. Он прислушивается к тому, что `События` испускаются командной стороной, и обрабатывает их в любой форме, которая имеет больше всего смысла (например, табличное представление).

В этом конкретном примере сторона запроса просто создает и поддерживает «материализованное представление» или «проекцию», в которой хранится последнее состояние отдельных продуктов (с точки зрения их идентификатора и описания, а также того, можно ли их продавать). Сторона запроса может многократно реплицироваться для обеспечения масштабируемости, и сообщения, хранящиеся в очередях RabbitMQ, могут быть сделаны долговечными, поэтому они могут даже временно хранить сообщения от имени стороны запроса, если она не работает.

Как на стороне команды, так и на стороне запроса есть API REST, которые можно использовать для доступа к их возможностям.

Для получения дополнительной информации см. Документацию Axon, в которой описано, как Axon привносит CQRS и Event Sourcing в ваши Java-приложения, а также много подробностей о том, как он настроен и используется.

Запуск демо

Выполнить демонстрационный код очень просто, но сначала вам нужно установить на своем компьютере следующее программное обеспечение. Для справки я использую Ubuntu 16.04 в качестве моей ОС.

  • Докер (я использую v1.8.2)
  • Docker-compose (я использую v1.7.1)

Если у вас есть оба из них, вы можете запустить демонстрацию, следуя процессу, описанному ниже.

Если у вас уже есть MongoDB или RabbitMQ, отключите эти службы, прежде чем продолжить, чтобы избежать конфликта доступности портов.

Шаг 1: Получить файл конфигурации Docker-compose

В новой пустой папке на терминале выполните следующую команду, чтобы загрузить последний файл конфигурации docker-compose для этой демонстрации.

1
$ wget https://raw.githubusercontent.com/benwilcock/microservice-sampler/master/docker-compose.yml

Старайтесь не менять имя файла — по умолчанию Docker ищет файл с именем ‘docker-compose.yml’. Если вы измените имя, используйте ключ -f на следующем шаге.

Шаг 2: Запустите микросервисы

Поскольку мы используем docker-compose, запуск микросервисов — это просто случай выполнения следующей команды.

1
$ docker-compose up

Вы увидите множество результатов загрузки и регистрации в окне терминала по мере загрузки и запуска образов докера.

Всего существует шесть образов Docker, это «mongodb», «rabbitmq», «config», «discovery», «product-cmd-side» и «product-qry-side».

Если вы хотите в любое время увидеть, какие экземпляры Docker запущены на вашем компьютере, откройте отдельный терминал и выполните следующую команду: —

1
$ docker ps

Как только экземпляры будут запущены (это может занять некоторое время), вы можете сразу же осмотреться с помощью браузера. Вы должны иметь доступ к: —

  1. Консоль управления кроликом на порту `15672`
  2. Серверная консоль Eureka Discovery на порту `8761`
  3. Отображения сервера конфигурации на порт `8888`
  4. Документация API Swagger на стороне команды продукта для порта `9000`
  5. Пустое представление продукта на стороне запроса на порту `9001`

Шаг 3: Работа с продуктами

Все идет нормально. Теперь мы хотим проверить добавление продуктов. В этом ручном тесте системы мы выполним команду `add` для API REST на стороне команды.

Когда командная сторона обработала команду, возникает событие «ProductAdded», которое сохраняется в MongoDB и пересылается на сторону запроса через RabbitMQ. Затем сторона запроса обрабатывает это событие и добавляет запись для продукта в его материализованное представление (на самом деле это база данных H2 в памяти для этой простой демонстрации). После обработки события мы можем использовать микросервис на стороне запроса для поиска информации о новом продукте, который был добавлен. Выполняя эти задачи, вы должны наблюдать некоторые результаты регистрации в окне терминала docker-compose.

Шаг 3.1: Добавить новый продукт

Чтобы выполнить тестирование, нам сначала нужно открыть второе окно терминала, откуда мы можем выполнить несколько команд CURL, не останавливая составленные в Docker экземпляры, которые мы запускаем в первом окне.

Для целей этого теста мы добавим MP3-продукт в наш каталог с названием «Все великолепно». Для этого мы можем использовать REST API на стороне команды и выполнить его с помощью запроса POST следующим образом:

1
$ curl -X POST -v --header "Content-Type: application/json" --header "Accept: */*" "http://localhost:9000/products/add/1?name=Everything%20Is%20Awesome"

Если у вас нет «CURL», вы можете использовать свой браузер для добавления продукта, перейдя к простой веб-форме, которая поставляется с документацией Swagger командной строки .

Вы должны увидеть следующий ответ.

01
02
03
04
05
06
07
08
09
10
11
12
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9000 (#0)
> POST /products/add/1?name=Everything%20Is%20Awesome HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.47.0
> Content-Type: application/json```bash
> Accept: */*$ http://localhost:9000/products/1
< HTTP/1.1 201 Created
< Date: Thu, 02 Jun 2016 13:37:07 GMTThis
< X-Application-Context: product-command-side:9000
< Content-Length: 0
< Server: Jetty(9.2.16.v20160414)

Код ответа должен быть <HTTP / 1.1 201 Created. Это означает, что продукт MP3 «Все великолепно» был успешно добавлен в хранилище событий командной строки.

Шаг 3.2: Запрос нового продукта

Теперь давайте проверим, можем ли мы просмотреть продукт, который мы только что добавили. Для этого мы используем API на стороне запроса на порту `9001` и запускаем простой запрос ‘GET’.

1
$ curl http://localhost:9001/products/1

Вы должны увидеть следующий вывод. Это показывает, что микросервис на стороне запроса имеет запись для нашего недавно добавленного MP3-продукта. Продукт указан как не подлежащий продаже (товарный = ложный).

01
02
03
04
05
06
07
08
09
10
11
12
{
  name: "Everything Is Awesome",
  saleable: false,
  _links: {
    self: {
    },
  product: {
    }
  }
}

Это оно! Продолжайте и повторите тест, чтобы добавить еще несколько продуктов, если хотите, но будьте осторожны, не пытайтесь повторно использовать тот же идентификатор продукта, когда вы отправляете POST, иначе вы увидите ошибку.

Если вы знакомы с MongoDB, вы можете проверить базу данных, чтобы увидеть все события, которые вы создали. Точно так же, если вы знакомы с консолью управления RabbitMQ, вы можете видеть сообщения, проходящие между микросервисами на стороне команды и на стороне запроса.