Как известно, кто убьет дракона, тот сам становится драконом. Spring, как фреймворк общего назначения, был очень хорош на фоне java EE 10 лет назад. Но сейчас стал очень монструозным и тяжелым на подьем. Сегодня рассмотрим Vertx как фреймворк-основу для создания микросервисов.
Что такое Vertx?

По сути это легковесный фреймворк для создания событийно-ориентированных асинхронных приложений любой сложности: от сетевой утилиты до современного веб приложения или микросервисов; от игр до банков и все, что между этим. Среди компаний, которые его используют, числятся такие монстры как RedHat и Bosch.
Минимально возможной единицей деплоя является verticle(вертикл). Это некий аналог микросервиса. Вертиклы могут быть запущены в разных java машинах на разных физических хостах и общаться между собой через встроенную распределенную шину данных (event bus).
Vertx является фреймворком-полиглотом – это значит, что каждый вертикл может быть написан на своем языке программирования. Список поддерживаемых языков: Java, Kotlin, JavaScript, Groovy, Ruby, Scala.
Установка вертиклов возможна в уже запущенный Vertx с разных источников, таких как локальная папка, git или maven репозиторий без остановки или рестарта уже запущенных вертиклов.
Больше технической информации можно найти на http://vertx.io. Также в разделе http://vertx.io/docs помимо самой документации доступны несколько книг и ссылки на примеры кода на всех поддерживаемых языках.
Почему Vertx?
Итак, мы выяснили, «что же ты такое». Теперь давайте попробуем по��ять, почему именно Vertx?
Инфраструктура внутренних приложений банка очень разнородна. Для нашей организации требуется фреймворк с большим количеством готовых плагинов, компонентов, интеграций с другими системами, а также готовыми системами мониторинга и разворачивания в гетерогенной инфраструктуре. Vertx можно легко использовать как консольное приложение, так и в виде кластера внутри Kubernetes с функционалом высокой доступности для каждого verticle. Есть готовые образы docker и поддержка разных систем авторизации и хранения конфигураций.
Немаловажным является размер финального приложения, потребляемые ресурсы и время старта. Vetrtx позволяет писать приложения с высокой степенью параллельной обработки данных на не очень мощном железе. Также архитектура Vertx позволяет писать многопоточные приложения — просто.
Тестовое приложение
Для примера давайте напишем небольшой микросервис с минимальным rest api и метриками для Prometheus также доступными через web socket.
Функционал максимально простой (2 rest endpoint). Мы принимаем rest запрос, далее переадресовываем на сервер Московской биржи и формируем ответ клиенту. Также подключим micrometer метрики и будем публиковать их по встроенной шине данных в web socket.
Итак, начнем.
Идем на http://start.vertx.io, создаем проект с 4 модулями:
- Vert.x Web
- Vert.x Web Client
- Metrics using Micrometer
- SockJS Service Proxies
и загружаем к себе на машину – мы получили полностью готовый проект.
Если выполним в папке с пректом консольную команду:
./mvnw clean compile exec:java
то на http://localhost:8888 сможем увидеть приветствие: Hello from Vert.x!
Из коробки мы имеем уже готовый веб-сервер, шину данных и возможность поднять данный проект в кластере. И это при размере финального «толстого» jar в 7,5 мегабайт!
Ладно, хватит восхвалений, перейдем к написанию нашего микросервиса.
Шаг 1. Обработка HTTP запросов
В Vertx маршрутизация http запросов осуществляется с помощью Router. Назначить обработчики запросов очень просто:
Router router = Router.router(vertx); //создаем роутер router.get("/hello").handler(rc -> { //назначаем обработчик для GET запросов для пути /hello rc.response() .putHeader(HttpHeaders.CONTENT_TYPE, "text/plain") .end("Hello from Vert.x!"); }); vertx.createHttpServer() .requestHandler(router) //создаем http сервер и назначаем роутер обработчиком запросов .listen(8888, http -> {....
Теперь то же приветствие доступно по пути http://localhost:8888/hello.
Шаг 2. Пишем REST API
API будет состоять из 2-х точек с вызовом другого удаленного api московской биржи:
- получение списка облигаций РСХБ
- получение описания по любой облигации
Для этого создадим новый роутер, опишем все точки и вынесем этот код в отдельный метод:
private Router createRestRouter(WebClient webClient) { Router restApi = Router.router(vertx); restApi.get("/rshb_bonds").handler(rc -> { webClient .get(80, "iss.moex.com", "/iss/securities.json") .addQueryParam("q", "РСХБ") .send(response -> { rc.response() .putHeader(HttpHeaders.CONTENT_TYPE, "application/json") .end(processMoexBondsRequest(response.result().bodyAsJsonObject()).encodePrettily()); }); }); restApi.get("/rshb_bonds/:bondId").handler(rc -> { //часть url используется как параметр String bondId = rc.request().getParam("bondId"); webClient .get("/iss/securities/"+ bondId +".json") .send(response -> { rc.response() .putHeader(HttpHeaders.CONTENT_TYPE, "application/json") .end( processMoexBondDescriptionRequest(response.result().bodyAsJsonObject()) ); }); }); return restApi; }
Функции processMoexBondsRequest, processMoexBondDescriptionRequest можно посмотреть на github. Их реализация опущена для читаемости листинга. Так же пока опустим обработку ошибок.
А так же прикрепим роут c нашим api в главный роут с префиксом “/rest/api/v1”.
router.mountSubRouter("/rest/api/v1/", createRestRouter(webClient));
Самые внимательные читатели уже заметили, что для вызовов удаленного http rest сервиса используется встроенный http клиент. Это тоже модуль Vertx, скрывающий за собой целый пласт функционала, такого как: ssl, пул соединений, таймауты соединений и т.д. Создается обьект в едином стиле всего фреймворка:
WebClientOptions webClientOptions = new WebClientOptions(); webClientOptions //значения по умолчанию, это позволит не проставлять их при каждом вызове .setDefaultPort(80) .setDefaultHost("iss.moex.com"); WebClient webClient = WebClient.create(vertx, webClientOptions);
ВСЕ! мы создали api, который можно проверить по следующим url:
http://localhost:8888/rest/api/v1/rshb_bonds
http://localhost:8888/rest/api/v1/rshb_bonds/RU000A101WQ2
Шаг 3. Получение метрик приложения
Для получения метрик нашего приложения, мы используем уже включенный в состав приложения модуль Micrometer metrics, а для того, чтобы он заработал, нужно указать эту настройку при запуске Vertx. Но в данный момент мы задействуем для запуска стандартный класс io.vertx.core.Launcher, который по умолчанию использует пустые параметры запуска. Ничего страшного, расширим его и укажем наш класс, в качестве стартового в pom.xml
public class LauncherWithMetrics extends Launcher { public static void main(String[] args) { new LauncherWithMetrics().dispatch(args); // необходимо для корректной работы лаунчера } @Override public void beforeStartingVertx(VertxOptions options) { options.setMetricsOptions( new MicrometerMetricsOptions() .setPrometheusOptions(new VertxPrometheusOptions().setEnabled(true)) .setEnabled(true)); } }
Осталось добавить в роутер точку /metrics
router.route("/metrics").handler(PrometheusScrapingHandler.create());
Готово. Теперь мы можем подключить Prometheus сервер к нашему сервису и собирать метрики. И все, что для этого понадобилось – пара десятков строк кода.
Шаг 4. Демонстрация метрик в браузере
Далее представим, что мы хотим показывать эти метрики в режиме реального времени в браузере. Для этого добавим еще один вертикл, который будет публиковать метрики каждую секунду на шину:
public class MetricsBusPublisher extends AbstractVerticle { @Override public void start(Promise<Void> startPromise) { MetricsService metricsService = MetricsService.create(vertx); vertx.setPeriodic( 1000, h -> vertx.eventBus().publish("metrics", metricsService.getMetricsSnapshot().encode()) ); startPromise.complete(); } }
и обработчик в роутер для трансляции содержимого шины в веб сокет:
SockJSBridgeOptions opts = new SockJSBridgeOptions() .addOutboundPermitted(new PermittedOptions() .setAddress("metrics")); // В целях безопасности можно указать, какие очереди доступны для трансляции, а какие могут принимать сообщения из вебсокета. Фильтры возможны на основе регулярных выражений. router.mountSubRouter("/eventbus", SockJSHandler.create(vertx).bridge(opts));
Теперь можно запустить webSocket.html из корня проекта и посмотреть метрики в реальном времени.
Где найти проект
Проект доступен на github:
https://github.com/RshbExample/VertxFirstSteps.git
В заключение
На этом, думаю, можно закончить первое знакомство с Vertx. Будем рады увидеть комментарии, ответить на вопросы и получить обратную связь о дальнейшем развитии этого демо-проекта. Если эта тема заинтересует читателей – мы продолжим серию статей и значительно расширим его функционал и сделаем обработку ошибок вызова удаленных сервисов.
