文章
Vert.x 教程(四)
Vertx的类组成及实现细节
目录
到目前为止,我们已经掌握了如何编写、配置和部署Verticle,也掌握了如何处理阻塞式代码。通过对示例代码的日志进行观察与分析,我们也了解了Vert.x线程模型的一些关键元素。对Vert.x开发应用程序有了一个基本认识。现在是时候回顾和剖析Verticle了,看看它内部到底有什么。
Verticle及其环境#
Verticle对象本质上是两个对象的组合。
- Vertx对象,即该Verticle所属的Vert.x实例。
- Context对象,即记录Verticle的上下文信息的实例。Verticle借助它将事件分派到各个处理程序
为什么这么说,当然是看Verticle的源码得知:
通常每个JVM进程只需要一个Vert.x实例,Vert.x实例由多个Verticle共享。从
Vertx vertx = Vertx.vertx();我们可以得出这一个结论。 Vert.x实例提供了许多核心API来声明事件处理程序。前几章的示例代码中已经使用了Vertx.x的setTimer、setPeriodic、createHttpServer、deployVerticle等方法。
用户自定义的事件处理程序借助Verticle Context运行。Verticle Context使得
我们可以在Verticle与Eventloop上执行事件处理程序,从而遵循Vert.x线程模型。
Verticl Context细节#
Context的创建#
可以使用Vertx类中的getOrCreatContext()方法获取Verticle的Context对象。虽然Context对象几乎总是与Vertical关联,但是实际上也可以在Vertical之外创建Context对象。
Vertx vertx = Vertx.vertx();
vertx.getOrCreateContext()
.runOnContext(v -> logger.info("ABC"));
vertx.getOrCreateContext()
.runOnContext(v -> logger.info("123"));
getOrCreateContext()** 是否返回同一个 Context?**- 如果当前线程已有 Vert.x Context,则它返回相同的 Context。
- 如果当前线程没有 Context,则创建新的 Context。
- 由于
createAndRun()是 在 main 线程中调用的,而Vertx.vertx()刚刚创建了 Vert.x 实例,所以getOrCreateContext()可能返回同一个 EventLoop Context(Vert.x 复用了 Context)。
runOnContext()** 是如何执行的?**runOnContext()并不会立即执行任务,它会将任务提交到 对应 Context 绑定的 Event Loop,然后由 EventLoop 异步执行。- 如果两个
getOrCreateContext()返回的是同一个 Context,那么runOnContext()调度的任务就会 被同一个 EventLoop 线程执行。
Context的应用#
利用Context传递任意“键值”数据#
private static void transferData() {
Vertx vertx = Vertx.vertx();
Context ctx = vertx.getOrCreateContext();
ctx.put("foo", "bar");
ctx.runOnContext(v -> logger.info("foo = {}", (String) ctx.get("foo")));
}
利用Context进行异常处理#
private static void handleExceptions() {
Vertx vertx = Vertx.vertx();
Context ctx = vertx.getOrCreateContext();
ctx.exceptionHandler(t -> {
if ("Tada".equals(t.getMessage())) {
logger.info("Got a _Tada_ exception");
} else {
logger.error("Woops", t);
}
});
ctx.runOnContext(v -> {
throw new RuntimeException("Tada");
});
}
桥接Vert.x线程和非Vert.x线程#
由于Vertx不是一个框架,我们可能会使用不同的线程模型,如何将非Vert.x线程模型集成到Vert.x应用程序中,我们可以使用这个技巧:获取一个Verticle的Context,再调用其runOnContext方法。
@Override
public void start() {
Context context = vertx.getOrCreateContext();
new Thread(() -> {
try {
run(context);
} catch (InterruptedException e) {
logger.error("Woops", e);
}
}).start();
}
private void run(Context context) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
logger.info("I am in a non-Vert.x thread");
context.runOnContext(v -> {
logger.info("I am on the event-loop");
vertx.setTimer(1000, id -> {
logger.info("This is the final countdown");
latch.countDown();
});
});
logger.info("Waiting on the countdown latch...");
latch.await();
logger.info("Bye!");
}