Vert.x是一个基于Netty的异步编程工具集,它的核心包中提供了编写异步WEB应用的基础API。现在市面上大多数WEB框架依赖于简单的线程策略:每一个请求绑定一个线程,这个线程处理这个请求直到返回响应或失去连接,这种同步I/O模型虽然易于理解,但不仅增加了系统内核调度线程的开销,且各类I/O的阻塞会使得CPU资源无法充分被利用,极大影响了系统的效率,而基于异步I/O的Vert.x能够有效的避免这些问题
Verticle和Event Loop
Verticle是Vert.x部署运行的基本单元,而Event Loop是Vert.x用于调度CPU所使用的线程,Verticle通过将时间分派给Event Loop处理的方式来处理事件。
Verticle
Verticle通过开箱即用的方式提供了一个简单便捷的、可扩展的、类似 Actor Model 的部署和并发模型机制。 一个应用程序通常是由在同一个 Vert.x 实例中同时运行的许多 Verticle 实例组合而成。不同的 Verticle 实例通过向 Event Bus 上发送消息来相互通信
Verticle 种类
有三种不同类型的 Verticle:
- Stardand Verticle:这是最常用的一类 Verticle —— 它们永远运行在 Event Loop 线程上
- Worker Verticle:这类 Verticle 会运行在 Worker Pool 中的线程上。一个实例绝对不会被多个线程同时执行
- Multi-Threaded Worker Verticle:这类 Verticle 也会运行在 Worker Pool 中的线程上。一个实例可以由多个线程同时执行(译者注:因此需要开发者自己确保线程安全)
Event Loop
默认情况一个 Vert.x 实例维护了N(默认情况下N = CPU核数 x 2)个 Event Loop 线程。除了在Worker Verticle外,所有的处理器核只会在同一个Event Loop线程中被调用,永远不会被并发执行。在这种情况下,同时并发执行的Event Loop与CPU核心数直接关联,Event Loop线程的阻塞将会对应用性能产生更大的影响,因此,不要在业务实现中写阻塞代码,如果阻塞了 Vertx 实例中的所有 Event Loop,那么应用就会完全停止
运行阻塞式代码
在实际的开发过程中,还是不可避免的会使用到一些同步API,这些API中许多方法都是阻塞式的,但是如上所述,并不能够在Event Loop中直接调用这些阻塞式操作。现有以下两周方式可供选择
-
调用
executeBlocking
方法来执行阻塞式代码并处理结果的异步回调vertx.executeBlocking(future -> { // 调用一些需要耗费显著执行时间返回结果的阻塞式API String result = someAPI.blockingMethod("hello"); future.complete(result); }, res -> { System.out.println(res.result()); });
-
使用Worker Verticle
WorkerExecutor executor = vertx.createSharedWorkerExecutor("my-worker-pool"); executor.executeBlocking(future -> { // 调用一些需要耗费显著执行时间返回结果的阻塞式API String result = someAPI.blockingMethod("hello"); future.complete(result); }, res -> { System.out.println("The result is: " + res.result()); });
Worker Executor 在不需要的时候必须被关闭:
executor.close();
Context对象
当 Vert.x 传递一个事件给处理器或者调用 Verticle 的 start 或 stop 方法时,它会关联一个 Context 对象来执行。
- 在Stardand Verticle中:Context对象会绑定在一个特定的Event Loop线程上,所以在该Context上执行的操作一直会在同一个Event Loop线程中
- 在Worker Verticle中:Context是一个 Worker Context,并且所有的操作运都会运行在 Worker 线程池的线程上
- 一个 Context 对应一个 Event Loop 线程(或 Worker 线程),但一个 Event Loop(或 Worker 线程)可能对应多个 Context
Vert.x定时器(延时操作&定时)
vertx对象提供了setTimer方法和setPeriodic分别来进行延时操作和定时操作,这两个方法都接收一个延时时间和一个执行业务的Handler为参数,方法的返回值都为计时器的Id
延时操作(一次性)
通过vertx对象的setTimer方法来设置延时操作触发
long timerID = vertx.setTimer(1000, id -> {
System.out.println("And one second later this is printed");
});
System.out.println("First this is printed");
定时器
通过vertx对象的setPeriodic方法来设置定时器
long timerID = vertx.setPeriodic(1000, id -> {
System.out.println("And every second this is printed");
});
System.out.println("First this is printed");