Готовим микросервисы в Docker на Spring Boot + Rest + Kafka + MongoDB

Сегодня мы приготовим два микросервиса на Spring Boot и развернем их в Docker.  Про то что такое Docker написано здесь. Один микросервис будет отвечать за получение данных из вне и записывать их в нужном виде в Kafka. Про то как локально настроить у себя на компьютере Apache Kafka написано здесь. Второй микросервис будет считывать данные из Kafka и сохранять их в MongoDB.

MongoDBэто NoSQL база данных, поддерживающая реактивную модель программирования, рассчитана на большое количество insert-ов. Структура хранения данных в MongoDBBJSON – бинарный JSON. База является документоориентированной т.е. запись в базе это json документ с данными.

На этом превью можно заканчивать и начинать создавать микросервисы.

Начнем с нижнего уровня и создадим микросервис для записи данных. Для начала нам необходимо создать 3 отдельных проекта для нашей архитектуры. Два проекта – микросервисы producer и consumer, еще один проект будет содержать общие классы для двух микросервисов. Так сделано для удобства обновления, а так же для корректной работы собственноручно организованной сериализации. Об этом ниже. Создаем три проекта со следующими опциями:

В результате мы должны получить следующую структуру проекта:

Данные мы будем получать с помощью rest-клиента, после, полученные данные передавать в Kafka.  Для начала создадим RestTemplate для получения данных. Для этого нам понадобиться создать конфигурацию и сервисы для получения данных.

Далее необходимо в проекте common-libs создать DTO (Data Transfer Object) – это объект, который мы будем передавать из одного микросервиса в Kafka и другим читать. Воспользуемся для этого одним интересным приемом. Для начало вам понадобиться скачать и установить приложение Postman. Запустив, мы отправляем запрос “http://apifaketory.com/api/user?limit=3” сервер с которого будем получать данные.

Если все сделано правильно, то в секции body вы получите json файл с данными. Далее, необходимо создать java-классы для байндинга этого json в объекты. Для этого воспользуемся ресурсом http://www.jsonschema2pojo.org. Скопируйте один json объект из ответа postman, вставьте в левую часть как показано на рисунке и сделайте необходимые настройки. ОБЯЗАТЕЛЬНО сделайте классы сериализуемыми!!! После скачиваем архив с уже готовыми классами и добавляем в проект common-libs.

Дополнительно добавляем класс UserData следующего содержания:

После необходимо добавить в pom следующую запись:

И нажать install. Это даст нам возможность подключить зависимость common-libs в наши микросервисы.

Продолжим написание producer. Добавляем зависимость а pom и настирываем получение данных с помощью RestTemplate.

Теперь перейдем к конфигурации Kafka. Создадим три класса: в одном сделаем конфигурацию самой Kafka, во втором конфигурацию топиков и в третьем опишем сам процесс записи данных в Kafka.

Файл application.properties должен выглядеть так:

Отлично! Проверим как работает наша запись в Kafka. Проверьте, что Kafka запущена на вашем компьютере! Далее допишем отправку сообщений в Kafka в нашем главном классе и запустим.

Если вы все сделали правильно, то в логах у вас будет сообщение о том, что данные передались! Теперь упакуем наш сервис в Docker Image и запустим его внутри Docker – контейнера. Для этого нам необходимо собрать “толстый” джарник нашего приложения (так же нажать install, как в случае с commom-libs) и написать Dockerfile в корне проекта со следующим содержанием:

Далее необходимо проверить, что во всех ваших pom-файлах указано, что версия языка используется 8, несмотря на то что мы при старте проекта использовали Java 11. Для запуска в контейнере нам нужна именно Java 8.

Для common-libs так же нужно изменить конфигурацию плагина:

После того как вы создадите Dockerfile, IntelliJ IDEA предложит вам установить плагин для работы с Docker.  Воспользуйтесь этой возможностью, очень удобный инструмент. Теперь нам необходимо изменить конфигурацию нашей локальной Kafka, потому что теперь подключение к Kafka будет осуществляться из Docker-контейнера. Необходимо прописать ip – адрес в файл server.properties, а так же обновить конфигурационные файл в producer. IP-адрес можно узнать через консоль командой ipconfig для windows и netstat -rn для unix-систем. В файле server.properties, находим следующую строчку и прописываем ей свой ip – адрес.

Отлично, теперь собираем проект producer с помощью команды maven:install, настраиваем создание Docker Image и запуск контейнера либо через Intellij IDEA, либо командой:

Если все сделано правильно, то в логах контейнера можно увидеть, что мы записали сообщения в Kafka с из Docker контейнера!

Отлично! Мы написали наш первый микросервис и развернули его в Docker контейнере. Приступим к написанию второго микросервиса, потребителя (consumer) данных из Apache Kafka и записи в MongoDB. Для начала необходимо так же настроить Kafka.

Далее, создаем репозиторий для записи данных в MongoDB, а так же сервис для выполнения операций над MongoDB. Необходимо добавить несколько дополнительных зависимостей, а так же удалить блок <scope>  у внутренней MongoDB. В pom.xml добавляем следующие зависимости:

Осталось только запустить приложение потребителя, считать данные из Kafka и сохранить их в MongoDB.

Файл application.properties для consumer:

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

Запустим потребителя данных в контейнере по аналогии с предыдущим микросервисом. Путь тот же самый: создаем Dockerfile, собираем проект, запускаем. Если все сделано правильно, то в логах контейнера вы увидите следующее сообщение:

Выводы

Итак, мы создали два микросервиса: один с помощью Rest получает данные из внешнего источника, упаковывает их в объект и отправляет в Apache Kafka, второй микросервис читает данные из Apache Kafka и сохраняет их в MongoDB. Эти сервисы работают внутри Docker контейнеров.

Бонус

В данном примере мы использовали стандартный Json-сериализатор/десериализатор. Однако, его может вам не хватить. К примеру, если в вашем объекте передается большое количество данных, например, объекты с координатами графика. Таких точек может быть несколько тысяч и json-сериализатор отработает так, что ваш объект будет весить больше 100 мегабайт. При записи такого объекта в Kafka возникнет ошибка, т.к. в стандартной конфигурации сервера Kafka установлено ограничение на 100мб для сообщения. Из этой ситуации есть 3 выхода:

  1. Объекты, например, графики или любые другие объекты, содержащиеся в большом количестве, нужно предварительно кодировать и сжимать используя, например, base64. При таком подходе мы потеряем визуализацию данных в MongoDB, и для работы других приложений с этими данными нужно использовать декодировку.
  2. Увеличить размер максимума для сообщений в конфигурации Kafka. Для этого необходимо в файле server.properties изменить число в блоке socket.request.max.bytes.
  3. Написать для объекта свой собственный сериализатор/десериализатор. Про то что такое сериализация написано здесь.

Предпочтительным является третий путь, потому что он дает нам более гибкий инструмент для управления сериализацией собственных объектов. Реализуем этот путь в наших микросервисах. Для этого нам нужно создать два класса для сериализации и десериализации, реализующих интрефейсы Serializer и Deserializer пакета kafka.common.serialization.

И подставить новые сериализатор/десериализатор в конфигурационные классы Kafka обоих сервисов.

Исходный код со стандартным json-сериализатором лежит здесь

Исходный код с реализацией собственного сериализатора здесь

ПРИЯТНОГО АППЕТИТА!

Оставить комментарий:

Ваш email не будет опубликован.