第二章: Vert.x的基本处理单元:Verticles-2.4 什么才是真正的verticle?

2.4 什么才是真正的verticle?

到目前为止,您已经了解了如何编写verticles、如何部署和配置verticles,以及如何处理阻塞代码。通过在示例中使用信息日志,您已经了解了Vert.x线程模型的元素。

现在是时候回过头来仔细分析一下verticle里面是什么了,并确保你在本章结束时对verticle是如何工作的以及如何正确使用它们有了全面的了解。

2.4.1 Verticles及其environment

图 2.5 概述了一个 Verticle 和它的环境之间的关系。
一个verticle对象本质上是两个对象的组合:

  • Verticle 所属的 Vert.x 实例
  • 允许将事件分派给处理程序的专用上下文实例

Vert.x 实例公开了用于声明事件处理程序的核心 API。 我们已经在之前的代码示例中使用了它,其中包括 setTimersetPeriodiccreateHttpServerdeployVerticle 等方法。 Vert.x 实例被多个 Verticle 共享,每个 JVM 进程一般只有一个 Vert.x 实例。

上下文实例持有对线程的访问权以执行处理程序。 事件可能源自各种来源,例如计时器、数据库驱动程序、HTTP 服务器等。 因此,它们通常由其他线程触发,例如 Netty的accepting线程或计时器线程。

用户定义的回调中的事件处理通过上下文发生。 上下文实例允许我们在 Verticle 事件循环线程上调用处理程序,从而尊重 Vert.x 线程模型。
在这里插入图片描述
worker verticles的情况没有太大区别,只是处理程序是在工作线程池中使用一个工作线程执行的,如图2.6 所示。它们仍然是verticles,就像它们的事件循环一样,代码可以假定是单线程访问。只是没有固定的工作线程被用于处理一个worker verticle的事件。
在这里插入图片描述

2.4.2 有关上下文的更多信息

可以使用 Vert.x 类中的 getOrCreateContext() 方法访问上下文对象。 虽然上下文几乎总是与verticle相关联,但可以在verticle之外创建事件循环上下文。 正如该方法的名称所暗示的那样

  • 从像verticle这样的上下文线程调用getOrCreateContext()会返回上下文。
  • 从非上下文线程调用 getOrCreateContext() 会创建一个新上下文。

清单 2.18 显示了一个创建全局 Vertx 实例的示例,并且在 JVM 进程主线程上对 getOrCreateContext 进行了两次调用。 每次调用之后都会调用 runOnContext,它允许我们在上下文线程上运行代码块。
在这里插入图片描述
正如您在下一个清单中看到的,每个上下文都被分配给一个事件循环。
在这里插入图片描述
上下文对象支持更多操作,例如保存上下文范围的任意键/值数据和声明异常处理程序。 下面的清单显示了一个示例,其中 foo 键包含字符串 bar,并且声明了一个异常处理程序以在事件循环线程上执行处理程序时捕获和处理异常。
在这里插入图片描述

package chapter2.dissecting;

import io.vertx.core.Context;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ThreadsAndContexts {

  private static final Logger logger = LoggerFactory.getLogger(ThreadsAndContexts.class);

  public static void main(String[] args) {
    createAndRun();
    dataAndExceptions();
  }

  private static void createAndRun() {
    Vertx vertx = Vertx.vertx();

    vertx.getOrCreateContext()
      .runOnContext(v -> logger.info("ABC"));

    vertx.getOrCreateContext()
      .runOnContext(v -> logger.info("123"));
  }

  private static void dataAndExceptions() {
    Vertx vertx = Vertx.vertx();
    Context ctx = vertx.getOrCreateContext();
    ctx.put("foo", "bar");

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

    ctx.runOnContext(v -> {
      logger.info("foo = {}", (String) ctx.get("foo"));
    });
  }
}

当事件处理分布在多个类中时,上下文数据可能很有用。 否则,使用类字段要简单得多(而且速度更快!)。

当事件处理可能引发异常时,异常处理程序很重要。 默认情况下,异常仅由 Vert.x 记录,但在执行自定义操作以处理错误时,重写上下文异常处理程序很有用。
在这里插入图片描述
运行清单 2.20 中的代码会产生类似于清单 2.21 的输出。

2.4.3 桥接 Vert.x 和非 Vert.x 线程模型

在编写 Vert.x 应用程序时,您可能不必处理 Vert.x 上下文。 尽管如此,在一种情况下它最有意义:当您必须使用具有自己的线程模型的第三方代码,并且您希望使其与 Vert.x 一起正常工作时。

下一个清单中的代码显示了一个创建非 Vert.x 线程的示例。 通过传递从 Verticle 获得的上下文,我们能够从运行在非Vert.x线程上的代码里在事件循环中执行一些代码。
在这里插入图片描述

package chapter2.dissecting;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Context;
import io.vertx.core.Vertx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;

public class MixedThreading extends AbstractVerticle {

  private final Logger logger = LoggerFactory.getLogger(MixedThreading.class);

  @Override
  public void start() {
    Context context = vertx.getOrCreateContext();   // <1>
    new Thread(() -> {
      try {
        run(context);
      } catch (InterruptedException e) {
        logger.error("Woops", e);
      }
    }).start();                                      // <2>
  }

  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 -> {                       // <3>
      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!");
  }

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

<1>: 因为start在一个事件循环线程上运行,所以我们获得了该verticle的上下文。

<2>: 我们启动一个普通的Java线程。

<3>: runOnContext确保我们在verticle的事件循环线程上运行一些代码。

下面清单中的日志显示了这一点。
在这里插入图片描述
每当您需要将非 Vert.x 线程模型集成到您的应用程序中时,您都可以使用拥有verticle上下文并发出 runInContext 调用的方法。

💡提示: 这个例子展示了上下文的另一个重要属性: 在定义处理程序时传播上下文。实际上,使用runOnContext运行的代码块会在一秒钟后设置一个计时器处理程序。您可以看到,计时器处理程序在与用来定义它的上下文相同的上下文中执行。

下一章将讨论事件总线,这是Vertx应用程序中verticles可以相互通信和进行事件处理的标准方式。

总结

  • Verticle 是 Vert.x 应用程序中异步事件处理的核心组件。
  • Event-loop verticles 处理异步 I/O 事件,并且应该没有阻塞和长时间运行的操作。
  • Worker Verticle 可用于处理阻塞 I/O 和长时间运行的操作。
  • 通过使用事件循环上下文,可以将代码与 Vert.x 和非 Vert.x 线程混合。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值