Web-сервис, содержащий в себе простой бизнес-сценарий и примеры использования различных технологических инструментов, подходов к проектированию
- Введение
- Используемые инструменты
- Техническое наполнение
- Развертка
- Использование
- Использование развёрнутой системы
- Публикация
Проект содержит исходный код и docker-compose файл для развертки сервиса и окружения.
Основная функция сервиса - принимать из различных источников обьект с полями email/text, заполнять по нему шаблон email-письма и по планировщику отправлять на smtp сервер. Сервис имеет 2 операции, которые могут быть инициированы клиентами - создание письма и получение письма. Отправка письма на smtp сервер осуществляется асинхронно, по планировщику.
Процесс импорта проекта в IntellijIDEA не требует дополнительных настроек локальной среды, проект может быть импортирован в IDE с помощью меню File->New->Project from existing sources->Gradle
.
Сервис построен на фреймворках, поддерживающих Reactive Streams спецификацию:
- WebFlux - основной фреймворк сервиса, позволяющий запускать web-приложение в рамках веб-сервера Netty, который поддерживает реактивный способ обработки http-запросов. Также используются несколько дополнительных фрейворков экосистемы Spring, такие как Security, r2dbc-starter и т.д.
- Kotlin coroutines - библиотека расширения синтаксиса kotlin-языка, позволяющая использовать те же реактивные подходы, что и Project Reactor фреймворк (на котором построен WebFlux), при этом используя последовательный, императивный стиль программирования, не используя функциональный подход типов Mono/Flux.
- Reactor Kafka - драйвер для работы с брокером сообщений Kafka, использующий реактивный подход к обработке потребления и отправке событий. Позволяет не блокировать потоки в пуле, отвечающем за потребление и отправку сообщений до конца выполнения операции.
- Reactive Redis - реактивный драйвер для работы с распределенным кэш-хранилищем Redis.
- Reactive Postgres - реактивный драйвер для работы с СУБД Postgres.
Также, для отправки email-сообщений используется фреймворк Spring Mail, который основан на Jakarta Mail библиотеке и не поддерживает реактивность изначально. Для работы с этим фреймворком в неблокирующей среде используется динамический пул потоков Dispatchers.IO, который выделен библиотекой Kotlin Coroutines специально для подобных случаев. В перспективе этот пул можно будет заменить на виртуальные потоки из Project Loom.
Полный список используемых библиотек можно найти в файле libs.versions.toml
Сервис имеет следующие технические возможности:
- Обе операции, поддерживаемые сервисом представлены в виде 2 типов API - rsocket, http
- Поддерживается создание письма с помощью отправки сообщения в соответствующую очередь в Kafka, также по изменению статуса сообщения (при создании или отправке) сервис отправляет сообщение в другую очередь.
- Для rsocket/http настроена basic авторизация с использованием Spring Security фреймворка
- При взаимодействии по RSocket протоколу используется бинарный формат данных Hessian. Это позволяет уменьшить размер передаваемых по сети данных, но накладывает на сервис дополнительную ответственность по десериализации обьектов не из сырых строк (будь то xml или json), а из специального формата. В перспективе неплохо заменяется CBOR
- Настроен стэк мониторинга приложения - генерация метрик, логов и трейсов. Метрики доступны по http-эндпоинту, трейсы и логи периодически отправляются самим сервисом в системы мониторинга. Далее про это описано более подробно.
- Настроено jmx - подключение внутри docker-контейнера.
- Реализовано взаимодействие с персистентным хранилищем (Postgres), работа со схемой базы происходит с помощью Flyway-миграций.
- Реализовано использование распределенного кэша Redis для хранения созданных сообщений.
- Реализован распределенный планировщик, поддерживающий горизонтальное масштабирование с помощью библиотеки ShedLock
- Для имитации работы с сервисом добавлен планировщик LoadScheduler, который периодически отправляет запросы на API сервиса и сообщения в прослушиваемую очередь.
- Для описания http-спецификаций добавлена библиотека openapi При наличии гейтвея и достаточного количества партиций в kafka-очередях сервис поддерживает горизонтальное масштабирование и может использоваться в рамках kubernetes-кластера.
Кроме того, есть несколько особенностей github-репозитория:
- Настроено автоматическое обновление версии сервиса в gradle.properties файле по созданию релиза
- Настроено автоматическое прохождение проверок качества (синтаксический анализ кода, тесты, более обширный статический sonar-анализ)
- Настроена автоматическая сборка и выкладка docker-образа по созданию нового релиза в github. Результаты тестов и анализа кода доступны в Sonarcloud, docker-образ доступен в Docker registry
Наиболее удобно развернуть сервис и всё необходимое для демонстрации технических возможностей окружение используя Docker.
В корне проекта находится compose файл, который при наличии на локально машине Docker можно запустить либо используя IDE (к примеру Docker плагин для IJIdea), либо из консоли следующей командой
docker-compose up -d
Команду необходимо исполнять находясь в папке с файлом docker-compose.yml.
Compose файл включает в себя следующие инструменты, каждый из которых доступен для подключения по localhost:
- Postgres - localhost:7000
- PGAdmin - localhost:4002
- Zookeeper - localhost:2181
- Kafka - localhost:29092
- Kafka-UI - localhost:9001
- Schema Registry (будет использоваться в будущем) - localhost:6005
- ELK Stack (без Beats) - localhost:9200(elastic), localhost:5601(kibana), localhost:25826(logstash), localhost:5046(logstash)
- Opensearch trace analytics - localhost:5601
- Redis - localhost:6379
- MailSlurper (smtp сервер, на который отправляются сообщения) - localhost:2500/8085/8083
- Mock server (http сервер с заглушкой по адресу GET /wheather, на текущий момент не используется) - localhost:5001
- Initializer - системный контейнер, который инициализирует data view внутри Kibana и завершает свое выполнение
- Сам сервис - localhost:8080, localhost:9091, localhost:7001, localhost:9010
Важно! Запуск потребует скачивания недостающих образов для запуска всех контейнеров и 8 ГБ оперативной памяти в процессе работы развёртки. Кроме того, вышеперечисленные порты должны быть свободны для корректной развёртки всего окружения. Перечислим их ещё раз одним списком:
7000, 4002, 2181, 29092, 9001, 6005, 9200, 5601, 25826, 5046, 3000, 9090, 9411, 6379, 2500, 8085, 8083, 5001, 8080, 9091, 7001, 9010
Сервис может разворачиваться и локально, с использованием собранного исходного кода вместо docker-образа.
Для этого необходимо изначально провести развертку с использованием Docker compose файла, после этого удалить контейнер с сервисом (service), собрать проект и запустить его либо через IDE, либо внеся изменения в docker compose файл - раскомментировать строки build/context контейнера service и убрать настройку image. Для запуска через IDE (например для проведения debug-а) необходимо прописать системную переменную contour.database.port=7000
Доступные инструменты окружения:
- Kibana с предустановленным data view
- Grafana с Micrometer дашбордом - admin/admin авторизация, дашборд находится в меню Dashboards
- Kafka UI - позволяет также отправить ручные сообщения в очереди
- Openapi UI с описанием http-API сервиса - авторизация user/password
- PGAdmin - мастер-пароль 123, пароль для подключения к базе mail_service
- Redis - Используется программа AnotherRedisDesktopManager, ссылка ведёт на один из последних её релизов. Подключение localhost:6379, пароль 123
- Smtp сервер
- Actuator API сервиса
- JMX подключение - Используется программа VisualVM. Необходимо добавить локальное jmx соединение, адрес localhost:9010
Liveness
и readiness
API доступны по actuator/health/liveness
и actuator/health/readiness
путям.
Также для ручного вызова доступны API сервиса по адресам localhost:7001
(rsocket, websocket соединение), localhost:8080
(http).
Для работы с rsocket рекомендуется использовать Postman, для работы с http - Opanapi ui.
Важно! - rsocket API использует формат данных Hessian, поэтому постман в ответе на websocket-сообщение отобразит только сырые байты
Когда проект собирается с использованием build
задачи gradle detekt и ktlint проверки проходят автоматически. Detekt xml отчет формируется по путям ./build/app/reports/detekt
, ./build/api/reports/detekt
.
Также есть возможность запускать проверки вручную командой
.\gradlew detekt
Тестирование и измерение покрытия также проходят автоматически при вызове команды build
. Покрытие измеряется инструментом kover, который в свою очередь использует движок JaCoCo.
Отчеты по покрытию (xml для sonar-инструментов и html для локальной разработки)
формируются по путям ./build/app/reports/kover/report.xml
, ./build/app/reports/kover/html/index.html
.
Также есть возможность вызвать тестирование и измерение покрытия вручную, вызвав команду
.\gradlew test koverPrintCoverage
Процент покрытия также можно смотреть и в самой IDE.
Для этого достаточно правой кнопкой мыши кликнуть на папку test и запустить задачу Run with Coverage.
Важно! Тесты не пройдут, если одновременно в докере развернут compose!
Прохождение Quality Gate реализовано с использованием gradle плагина sonarqube. Вызвать прохождение можно командами:
.\gradlew build
.\gradlew sonar -D"sonar.host.url"="<SONAR_HOST>" -D"sonar.token"="YOUR_TOKEN" -D"sonar.projectKey"="KEY" -D"sonar.organization"="ORG"
При вызове sonar с помощью gradle задачи сгенерированный detekt отчет и kover отчет будут добавлен к анализу.
Для тестирования будет полезна возможность локальной публикации пакетов в mavenLocal.
Для этого используется команда publishToMavenLocal.
.\gradlew publishToMavenLocal
В результате выполнения команды в .m2 папке пользователя появится артефакт mail-service-api, который будет содержать все
необходимые dto-классы, proto-файлы и другие нужные для взаимодействия с сервисом интерфейсы.