1. Обзор
В этой статье мы обсудим Vert.x , рассмотрим его основные концепции и создадим с его помощью простой веб-сервис RESTfull.
Мы начнем с рассмотрения основных концепций набора инструментов, постепенно перейдем к HTTP-серверу, а затем создадим службу RESTfull.
2. О Vert.x
Vert.x — это реактивный и многоязычный набор инструментов для разработки программного обеспечения с открытым исходным кодом от разработчиков Eclipse.
Реактивное программирование — это парадигма программирования, связанная с асинхронными потоками, которые реагируют на любые изменения или события.
Точно так же Vert.x использует шину событий для связи с различными частями приложения и асинхронно передает события обработчикам, когда они доступны.
Мы называем его полиглотом из-за поддержки нескольких языков JVM и других языков, таких как Java, Groovy, Ruby, Python и JavaScript.
3. Настройка
Чтобы использовать Vert.x, нам нужно добавить зависимость Maven:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>3.4.1</version>
</dependency>
Последнюю версию зависимости можно найти здесь .
3. Вертикали
Verticles — это фрагменты кода, которые выполняет движок Vert.x. Инструментарий предоставляет нам множество абстрактных классов вершин, которые можно расширять и реализовывать по своему усмотрению.
Будучи полиглотом, вершины могут быть написаны на любом из поддерживаемых языков. Приложение обычно состоит из нескольких вершин, работающих в одном и том же экземпляре Vert.x, и взаимодействует друг с другом с помощью событий через шину событий.
Чтобы создать вертикул в JAVA, класс должен реализовать интерфейс io.vertx.core.Verticle
или любой из его подклассов.
4. Шина событий
Это нервная система любого приложения Vert.x.
Будучи реактивными, вершины остаются бездействующими, пока не получат сообщение или событие. Вертиклы взаимодействуют друг с другом через шину событий. Сообщение может быть чем угодно, от строки до сложного объекта.
Обработка сообщений в идеале является асинхронной, сообщения ставятся в очередь на шину событий, а управление возвращается отправителю. Позже он удаляется из очереди на слушающую вертикул. Ответ отправляется с использованием методов Future
и обратного вызова
.
5. Простое приложение Vert.x
Давайте создадим простое приложение с вертиклом и развернем его с помощью экземпляра вертекса
. Чтобы создать нашу вертикаль, мы расширим
Чтобы создать нашу вершину, мы расширим класс io.vertx.core.AbstractVerticle
и переопределим метод start()
:
public class HelloVerticle extends AbstractVerticle {
@Override
public void start(Future<Void> future) {
LOGGER.info("Welcome to Vertx");
}
}
Метод start()
будет вызываться экземпляром вершины
при развертывании вершины. Метод принимает io.vertx.core.Future
в качестве параметра, который можно использовать для определения статуса асинхронного развертывания вертикали.
Теперь давайте развернем вертикул:
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new HelloVerticle());
}
Точно так же мы можем переопределить метод stop()
из класса AbstractVerticle
, который будет вызываться при закрытии вертикла:
@Override
public void stop() {
LOGGER.info("Shutting down application");
}
6. HTTP-сервер
Теперь давайте запустим HTTP-сервер, используя вертикул:
@Override
public void start(Future<Void> future) {
vertx.createHttpServer()
.requestHandler(r -> r.response().end("Welcome to Vert.x Intro");
})
.listen(config().getInteger("http.port", 9090),
result -> {
if (result.succeeded()) {
future.complete();
} else {
future.fail(result.cause());
}
});
}
Мы переопределили метод start()
для создания HTTP-сервера и прикрепили к нему обработчик запросов. Метод requestHandler()
вызывается каждый раз, когда сервер получает запрос.
Наконец, сервер привязывается к порту, и методу listen()
передается обработчик AsyncResult<HttpServer>
независимо от того, удалось ли установить соединение или запустить сервер с помощью future.complete()
или future.fail()
в методе случае каких-либо ошибок.
``
Обратите внимание: метод config.getInteger()
считывает значение конфигурации HTTP-порта, которое загружается из внешнего файла conf.json
.
Протестируем наш сервер:
@Test
public void whenReceivedResponse_thenSuccess(TestContext testContext) {
Async async = testContext.async();
vertx.createHttpClient()
.getNow(port, "localhost", "/", response -> {
response.handler(responseBody -> {
testContext.assertTrue(responseBody.toString().contains("Hello"));
async.complete();
});
});
}
Для теста воспользуемся vertx-unit вместе с JUnit.:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-unit</artifactId>
<version>3.4.1</version>
<scope>test</scope>
</dependency>
Мы можем получить последнюю версию здесь .
Вертикаль развертывается и находится в экземпляре вершины в методе
setup()
модульного теста:
@Before
public void setup(TestContext testContext) {
vertx = Vertx.vertx();
vertx.deployVerticle(SimpleServerVerticle.class.getName(),
testContext.asyncAssertSuccess());
}
Точно так же экземпляр вершины
закрывается в методе tearDown() @AfterClass
:
@After
public void tearDown(TestContext testContext) {
vertx.close(testContext.asyncAssertSuccess());
}
Обратите внимание, что метод setup()
@BeforeClass принимает аргумент TestContext
. Это помогает контролировать и тестировать асинхронное поведение теста. Например, развертывание вертикалей является асинхронным, поэтому в основном мы не можем ничего протестировать, если оно не развернуто правильно.
У нас есть второй параметр метода deployVerticle()
, testContext.asyncAssertSuccess().
Это
используется, чтобы узнать, правильно ли развернут сервер и произошли ли какие-либо сбои. Он ожидает вызова future.complete() или future.fail()
в вершине сервера. В случае сбоя он не проходит тест.
7. RESTful веб-сервис
Мы создали HTTP-сервер, давайте теперь используем его для размещения RESTfull WebService. Для этого нам понадобится еще один модуль Vert.x с именем vertx-web . Это дает множество дополнительных возможностей для веб-разработки поверх vertx-core .
Давайте добавим зависимость в наш pom.xml:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>3.4.1</version>
</dependency>
Мы можем найти последнюю версию здесь .
7.1. Роутер
и маршруты
Давайте создадим маршрутизатор
для нашего веб-сервиса. Этот маршрутизатор выберет простой маршрут метода GET и метод обработчика getArtilces()
:
Router router = Router.router(vertx);
router.get("/api/foreach/articles/article/:id")
.handler(this::getArticles);
Метод getArticle()
— это простой метод, который возвращает новый объект статьи :
private void getArticles(RoutingContext routingContext) {
String articleId = routingContext.request()
.getParam("id");
Article article = new Article(articleId,
"This is an intro to vertx", "foreach", "01-02-2017", 1578);
routingContext.response()
.putHeader("content-type", "application/json")
.setStatusCode(200)
.end(Json.encodePrettily(article));
}
Маршрутизатор ,
получив запрос, ищет соответствующий маршрут и передает запрос дальше. Маршруты ,
имеющие связанный с ним метод обработчика для суммирования запроса.
В нашем случае обработчик вызывает метод getArticle()
. Он получает объект routingContext
в качестве аргумента. Получает идентификатор
параметра пути и создает с ним объект статьи .
В последней части метода давайте вызовем метод response() для объекта
routingContext
и поместим заголовки, установим код ответа HTTP и завершим ответ, используя объект статьи в кодировке JSON.
7.2. Добавление маршрутизатора
к серверу
Теперь добавим маршрутизатор,
созданный в предыдущем разделе, к HTTP-серверу:
vertx.createHttpServer()
.requestHandler(router::accept)
.listen(config().getInteger("http.port", 8080),
result -> {
if (result.succeeded()) {
future.complete();
} else {
future.fail(result.cause());
}
});
Обратите внимание, что мы добавили requestHandler(router::accept)
на сервер. Это указывает серверу вызывать метод accept()
объекта маршрутизатора
при получении любого запроса.
Теперь давайте проверим наш WebService:
@Test
public void givenId_whenReceivedArticle_thenSuccess(TestContext testContext) {
Async async = testContext.async();
vertx.createHttpClient()
.getNow(8080, "localhost", "/api/foreach/articles/article/12345",
response -> {
response.handler(responseBody -> {
testContext.assertTrue(
responseBody.toString().contains("\"id\" : \"12345\""));
async.complete();
});
});
}
8. Упаковка приложения Vert.x
Чтобы упаковать приложение в виде развертываемого Java-архива (.jar), давайте воспользуемся подключаемым модулем Maven Shade и конфигурациями в теге выполнения :
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>io.vertx.core.Starter</Main-Class>
<Main-Verticle>com.foreach.SimpleServerVerticle</Main-Verticle>
</manifestEntries>
</transformer>
</transformers>
<artifactSet />
<outputFile>
${project.build.directory}/${project.artifactId}-${project.version}-app.jar
</outputFile>
</configuration>
В manifestEntries
Main-Verticle
указывает начальную точку приложения, а Main-Class
— это класс Vert.x, который создает экземпляр vertx
и развертывает Main-Verticle.
9. Заключение
В этой вводной статье мы обсудили инструментарий Vert.x и его основные концепции. Увидел, как создать HTTP-сервер с помощью Vert.x, а также RESTFull WebService, и показал, как их тестировать с помощью vertx-unit
.
Наконец-то упаковал приложение в виде исполняемого jar-файла.
Полная реализация фрагментов кода доступна на GitHub .