1. 概述

本文将深入探讨 Vert.x 框架,解析其核心概念,并手把手教你构建一个简单的 RESTful Web 服务。我们将从基础概念切入,逐步过渡到 HTTP 服务器搭建,最终完成完整的 RESTful 服务实现。

2. Vert.x 是什么

Vert.x 是 Eclipse 开源的一款反应式、多语言支持的开发工具包。它专为构建高性能、可伸缩的应用而设计。

反应式编程是一种基于异步流的编程范式,专注于对变化和事件的响应。Vert.x 通过事件总线实现组件间通信,当事件发生时异步传递给处理器。

我们称其为多语言工具包,因为它同时支持 JVM 和非 JVM 语言,包括:

  • ✅ Java
  • ✅ Groovy
  • ✅ Ruby
  • ✅ Python
  • ✅ JavaScript

3. 环境搭建

使用 Vert.x 需要添加 Maven 依赖:

<dependency>
    <groupId>io.vertx</groupId>
    <artifactId>vertx-core</artifactId>
    <version>3.9.15</version>
</dependency>

最新版本可在 Maven 中央仓库 查询。

4. Verticle 组件

Verticle 是 Vert.x 引擎执行的基本代码单元。工具包提供了多个抽象 verticle 类,可按需扩展实现。

得益于多语言特性,Verticle 可用任何支持的语言编写。典型应用由多个在同一 Vert.x 实例中运行的 Verticle 组成,通过事件总线进行事件通信。

在 Java 中创建 Verticle,需实现 io.vertx.core.Verticle 接口或其子类:

public class HelloVerticle extends AbstractVerticle {

    @Override
    public void start(Future<Void> future) {
        LOGGER.info("Welcome to Vertx");
    }
}

5. 事件总线

事件总线是 Vert.x 应用的神经系统。作为反应式框架,Verticle 在接收到消息或事件前保持休眠状态。组件间通过事件总线通信,消息可以是字符串或复杂对象。

消息处理本质是异步的:

  1. 消息进入事件总线队列
  2. 发送方立即获得控制权
  3. 消息出队传递给监听 Verticle
  4. 响应通过 Future 和回调机制返回

6. 简单 Vert.x 应用

我们来创建一个包含 Verticle 的简单应用,并通过 vertx 实例部署它。创建 Verticle 需继承 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 的代码:

public static void main(String[] args) {
    Vertx vertx = Vertx.vertx();
    vertx.deployVerticle(new HelloVerticle());
}

同样可重写 stop() 方法(关闭时调用):

@Override
public void stop() {
    LOGGER.info("Shutting down application");
}

7. 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() 创建服务器并附加请求处理器
  • requestHandler() 在每次收到请求时触发
  • 服务器绑定端口后,通过 listen() 的回调判断启动状态
  • config.getInteger() 从外部 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 部署:

@Before
public void setup(TestContext testContext) {
    vertx = Vertx.vertx();

    vertx.deployVerticle(SimpleServerVerticle.class.getName(), 
      testContext.asyncAssertSuccess());
}

关闭 Vert.x 实例:

@After
public void tearDown(TestContext testContext) {
    vertx.close(testContext.asyncAssertSuccess());
}

⚠️ 测试要点@Before 方法接收 TestContext 参数,用于控制异步行为。deployVerticle() 的第二个参数 testContext.asyncAssertSuccess() 会等待 Verticle 中的 future.complete()future.fail() 调用,失败时自动终止测试。

8. RESTful Web 服务

基于 HTTP 服务器构建 RESTful 服务,需要额外引入 vertx-web 模块:

<dependency>
    <groupId>io.vertx</groupId>
    <artifactId>vertx-web</artifactId>
    <version>3.9.15</version>
</dependency>

8.1. 路由器与路由

创建路由器并定义 GET 路由:

Router router = Router.router(vertx);
router.get("/api/baeldung/articles/article/:id")
  .handler(this::getArticles);

处理器方法实现:

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));
}

执行流程:

  1. 路由器匹配请求路径
  2. 调用关联的处理器方法
  3. RoutingContext 提取路径参数 id
  4. 构建 Article 对象
  5. 设置响应头、状态码并返回 JSON

8.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() 方法。

测试 REST 服务:

@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();
        });
      });
}

9. 应用打包

使用 Maven Shade 插件打包为可执行 JAR:

<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>

配置说明:

  • Main-Verticle:应用入口 Verticle
  • Main-Class:Vert.x 启动器类,负责创建 vertx 实例并部署主 Verticle

10. 总结

本文系统介绍了 Vert.x 工具包及其核心概念,通过实战演示了:

  • HTTP 服务器搭建
  • RESTful Web 服务开发
  • 使用 vertx-unit 进行测试
  • 打包为可执行 JAR

Vert.x 的反应式模型和事件驱动架构,使其成为构建高性能服务的利器。掌握这些基础后,可以进一步探索其分布式特性、集群支持等高级功能。