在前两个教程中对Vert.x开发Web应用已经有一个大概的了解,本章内容主要是讲述一些更深层次的内容,Vert.x的基本执行单元:Verticle。

📝 Verticle的执行细节

我们从一个简单的Verticle入手,示例代码如下:

实验一

public class HelloVerticle extends AbstractVerticle {

    private final Logger logger = LoggerFactory.getLogger(HelloVerticle.class);
    private long counter = 1L;

    @Override
    public void start() {
        // 创建一个定时作业
        vertx.setPeriodic(5000, id -> logger.info("tick"));
        // 创建一个http服务
        vertx.createHttpServer()
            .requestHandler(req -> {
                logger.info("Request #{} from {}", counter++, req.remoteAddress().host());
                req.response().end("Hello!");
            }).listen(8888)
            .onSuccess(server -> logger.info("HTTP server started on port: {}", server.actualPort()))
            // Print the problem on failure
            .onFailure(throwable -> logger.info("HTTP server start failure: {}", throwable.getMessage()));
    }

    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();
        //部署Verticle最简单的方式
        vertx.deployVerticle(new HelloVerticle());
    }
}

这段代码中我们创建了一个5秒的定时任务,打印一段日志,然后创建一个http的服务监听端口8888。运行main方法,运行结果如下:

image.png

从日志中可以看到,定时任务和Http服务的请求处理都是由vert.x-eventloop-thread-0完成的。

实验二

我们继续实验,修改代码如下所示,连续部署两个HelloVeticle

public class HelloVerticle extends AbstractVerticle {

    private final Logger logger = LoggerFactory.getLogger(HelloVerticle.class);
    private long counter = 1L;

    @Override
    public void start() {
        // 创建一个定时作业
        vertx.setPeriodic(5000, id -> logger.info("tick"));
        // 创建一个http服务器
        vertx.createHttpServer()
            .requestHandler(req -> {
                logger.info("Request #{} from {}", counter++, req.remoteAddress().host());
                req.response().end("Hello!");
            }).listen(8888)
            .onSuccess(server -> logger.info("HTTP server started on port: {}", server.actualPort()))
            // Print the problem on failure
            .onFailure(throwable -> logger.info("HTTP server start failure: {}", throwable.getMessage()));
    }

    public static void main(String[] args) {
        Vertx vertx = Vertx.vertx();
        //部署Verticle最简单的方式
        vertx.deployVerticle(new HelloVerticle());
        vertx.deployVerticle(new HelloVerticle());
    }
}

运行结果如下:

image.png

你可能会疑惑为什么没有端口占用冲突的错误。原因如下:

  1. Vert.x 默认启用了 multiple event-loop threads

    Vert.x 运行在一个 异步、事件驱动的模型 上,它使用了 多线程的事件循环。当你调用:

    
    vertx.deployVerticle(new HelloVerticle());
    vertx.deployVerticle(new HelloVerticle());
    

    你实际上启动了两个 HelloVerticle 实例,并且 它们在不同的 event loop 线程上运行。

  2. Vert.x 会自动处理端口共享

    在 Vert.x 的 HttpServer 实现中,多个 Verticle 可以 共享同一个端口,而不会报端口占用错误。这是因为:

  3. 请求会被多个 Verticle 处理

    由于多个 HelloVerticle 共享同一个 HTTP 服务器,当有请求进来时,它们会被 不同的 Verticle 实例轮流处理。这就像一个负载均衡器一样,分摊了 HTTP 请求的处理负载。

由上面两个实验,我们基本可以知道:一个Verticle实例运行在同一个线程上,我们可以安全的更新counter字段,我们不需要对counter进行任何同步操作,哪怕是在多线程环境下。

实验三