Как известно, кто убьет дракона, тот сам становится драконом. 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. Будем рады увидеть комментарии, ответить на вопросы и получить обратную связь о дальнейшем развитии этого демо-проекта. Если эта тема заинтересует читателей – мы продолжим серию статей и значительно расширим его функционал и сделаем обработку ошибок вызова удаленных сервисов.
