返回文章列表

文章

Vert.x 教程(四)

Vertx的类组成及实现细节

目录
  1. Verticle及其环境
  2. Verticl Context细节
  3. Context的创建
  4. Context的应用
  5. 利用Context传递任意“键值”数据
  6. 利用Context进行异常处理
  7. 桥接Vert.x线程和非Vert.x线程

到目前为止,我们已经掌握了如何编写、配置和部署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!");
  }