1. 概述
本文將討論 Vert.x,涵蓋其核心概念並使用它創建一個簡單的 RESTful Web 服務。
我們將首先介紹該工具包的核心概念,然後逐步過渡到 HTTP 服務器,最後構建 RESTful 服務。
2. 關於 Vert.x
Vert.x 是 Eclipse 開發團隊開源的、反應式和多語種軟件開發工具包
反應式編程是一種編程範式,與異步流相關聯,能夠響應任何變化或事件。
Vert.x 同樣使用事件總線,用於與應用程序的不同部分進行通信,並將事件異步地傳遞給處理程序,當處理程序可用時。
我們稱之為多語種,因為它支持 Java、Groovy、Ruby、Python 和 JavaScript 等多種 JVM 和非 JVM 語言
3. 安裝配置
要使用 Vert.x,我們需要添加 Maven 依賴項:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>3.9.15</version>
</dependency>最新版本的依賴項可以在這裏找到:這裏。
3. 虛擬實例
虛擬實例是 Vert.x 引擎執行的代碼片段。該工具包提供了許多可擴展和自定義的抽象虛擬實例類。
由於其多語言特性,虛擬實例可以編寫在任何受支持的語言中。一個應用程序通常由多個虛擬實例在同一 Vert.x 實例中運行,並通過事件總線進行互通信。
要創建 JAVA 虛擬實例,該類必須實現 io.vertx.core.Verticle 接口,或其任何子類。
4. 事件總線 (Event Bus)
它是任何 Vert.x 應用的神經系統。
由於其反應式特性,Vert.x 實例在接收到消息或事件時保持休眠狀態。Vert.x 實例通過事件總線與其他實例進行通信。消息可以是字符串到複雜對象,任何類型。
消息處理應儘可能異步進行。消息被排隊到事件總線上,控制權返回給發送者。稍後,它會被從監聽器 Vert.x 實例中取出。響應使用 Future 和 callback 方法發送。
5. 簡單 Vert.x 應用程序
讓我們創建一個簡單的應用程序,其中包含一個 Vert.x 模塊,並使用 vertx 實例進行部署。 要創建我們的 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() 方法將在 verticle 部署時由 vertx 實例調用。該方法接受 io.vertx.core.Future 作為參數,可用於發現 verticle 異步部署的狀態。
現在讓我們部署 verticle:
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 服務器
現在我們使用 verticle 啓動一個 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() 方法在服務器收到請求時每次都會被調用。
最後,服務器綁定到端口,並且使用 future.complete() 或 future.fail() 在連接或服務器啓動成功或發生錯誤的情況下,將 AsyncResult<HttpServer> 處理程序傳遞給 listen() 方法。
請注意: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.9.15</version>
<scope>test</scope>
</dependency>我們可以從這裏獲取最新版本。
Verticle 已部署並在 vertx 實例的 setup() 方法中:
@Before
public void setup(TestContext testContext) {
vertx = Vertx.vertx();
vertx.deployVerticle(SimpleServerVerticle.class.getName(),
testContext.asyncAssertSuccess());
}同樣,vertx實例在@AfterClass tearDown()方法中被關閉:
@After
public void tearDown(TestContext testContext) {
vertx.close(testContext.asyncAssertSuccess());
}請注意,@BeforeClass setup() 方法接受一個 TestContext 參數。這有助於我們控制和測試測試中的異步行為。例如,Verticle部署是異步的,因此我們無法測試它是否正確部署。
我們有第二個參數傳遞給 deployVerticle() 方法,即 testContext.asyncAssertSuccess() 。它用於確定服務器是否已正確部署或是否發生任何錯誤。它會等待 future.complete() 或 future.fail() 在服務器 Verticle 中被調用。如果發生錯誤,則測試會失敗。
7. RESTful WebService
我們已經創建了一個 HTTP 服務器,現在將使用它來託管一個 RESTful WebService。為此,我們需要另一個 Vert.x 模塊,即 vertx-web。它為在 vertx-core 的基礎上進行 Web 開發提供了許多額外的功能。
讓我們將依賴項添加到我們的 pom.xml 中:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>3.9.15</version>
</dependency>我們可以在這裏找到最新版本 這裏。
7.1. 路由器和路由
讓我們為我們的 WebService 創建一個 路由器。這個路由器將處理一個簡單的 GET 方法路由,以及 handler 方法 getArtilces()。
Router router = Router.router(vertx);
router.get("/api/baeldung/articles/article/:id")
.handler(this::getArticles);getArticle() 方法是一個簡單的方法,它返回一個新的 Article 對象:
private void getArticles(RoutingContext routingContext) {
String articleId = routingContext.request()
.getParam("id");
Article article = new Article(articleId,
"This is an intro to vertx", "baeldung", "01-02-2017", 1578);
routingContext.response()
.putHeader("content-type", "application/json")
.setStatusCode(200)
.end(Json.encodePrettily(article));
}一個 路由器, 在接收到請求時,會查找匹配的路由並將其傳遞給後續處理。 路由中包含與該請求關聯的處理方法,用於對請求進行處理。
在我們的例子中,處理方法會調用 getArticle() 方法。 它接收 routingContext 對象作為參數,並從中提取路徑參數 id,然後創建一個包含該參數的 Article 對象。
在方法最後一部分,我們調用 response() 方法,該方法位於 routingContext 對象上,用於設置頭部、設置 HTTP 響應代碼,並使用 JSON 編碼的 Article 對象結束響應。
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() 函數,該函數屬於 router 對象。
現在,讓我們測試我們的 WebService:
@Test
public void givenId_whenReceivedArticle_thenSuccess(TestContext testContext) {
Async async = testContext.async();
vertx.createHttpClient()
.getNow(8080, "localhost", "/api/baeldung/articles/article/12345",
response -> {
response.handler(responseBody -> {
testContext.assertTrue(
responseBody.toString().contains("\"id\" : \"12345\""));
async.complete();
});
});
}8. 部署式 Java 存檔 (JAR) 包 Vert.x 應用程序
要將應用程序打包為部署式 Java 存檔 (JAR) 文件,我們使用 Maven Shade 插件以及在 <em execution</em>> 標籤中的配置:
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>io.vertx.core.Starter</Main-Class>
<Main-Verticle>com.baeldung.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 工具包及其基本概念。演示瞭如何使用 Vert.x 創建 HTTP 服務器,以及如何使用 Vert.x 構建 RESTful Web 服務,並展示瞭如何使用 vertx-unit 進行測試。
最後,將應用程序打包為可執行的 JAR 文件。