Verticle

个人理解

  • vertx 就我学到这里有很多实现vertx 模型的对象如 文件vertx httpwertx websocketvert
  • Verticle 应该指的就是自定义实现vertx 操作的对象。

Verticle

如下 需要继承AbstractVerticle 或 实现

public class MyVerticle extends AbstractVerticle {
 // Verticle部署时调用
 public void start() {
 }
 // 可选 - Verticle撤销时调用
 public void stop() {
 }
}
  • 有些时候您的 Verticle 启动会耗费一些时间,您想要在这个过程做一些事, 并且您做的这些事并不想等到Verticle部署完成过后再发生。例如,您想在 start 方法中启动一个 HTTP 服务并在 listen 方法中处理一个异步结果。
  • 可以实现 异步版本 的 start 方法来实现,它接收一个 Promise 参数。 方法执行完时,Verticle 实例并没有部署好(状态不是 deployed)。
public class MyVerticle extends AbstractVerticle {

 private HttpServer server;

 public void start(Promise<Void> startPromise) {
   server = vertx.createHttpServer().requestHandler(req -> {
     req.response()
       .putHeader("content-type", "text/plain")
       .end("Hello from Vert.x!");
     });
   // Now bind the server:
   server.listen(8080, res -> {
     if (res.succeeded()) {
       startPromise.complete();
     } else {
       startPromise.fail(res.cause());
     }
   });
 }
}

同理stop 也可以接受一个Promise 对象

Verticle 种类

  1. Standard Verticles
    当 Standard Verticle 被创建时,它会被分派给一个 Event Loop 线程,并在这个 Event Loop 中执行它的 start 方法。 当您在一个 Event Loop 上调用了 Core API 中的方法并传入了处理器时,Vert.x 将保证用与调用该方法时相同的 Event Loop 来执行这些处理器。
    这意味着我们可以保证您的 Verticle 实例中 所有的代码都是在相同Event Loop中执行 (只要您不创建自己的线程来调用它!)
    同样意味着您可以将您的应用中的所有代码用单线程方式编写,让 Vert.x 去考虑线程和扩展问题。您不用再考虑 synchronized 和 volatile 的问题, 也可以避免传统的多线程应用经常会遇到的竞态条件和死锁的问题。
  2. Worker Verticles
    worker Verticle 和 Standard Verticle 很像,但它并不是由一个 Event Loop 来执行, 而是由 Vert.x 中的 Worker Pool 中的线程执行。
    Worker Verticle 设计用于调用阻塞式代码,它不会阻塞任何 Event Loop。
    如果您不想使用 Worker Verticle 来运行阻塞式代码, 您还可以在一个Event Loop中直接使用 内联阻塞式代码,您需要通过 setWorker方法来将 Verticle 部署成一个 Worker Verticle:
DeploymentOptions options = new DeploymentOptions().setWorker(true);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);

Verticle部署和撤销

部署

  • Verticle是异步部署的,换而言之,可能在 deploy 方法调用返回后一段时间才会完成部署。如果您想要在部署完成时收到通知,则可以指定一个完成处理器:
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", res -> {
  if (res.succeeded()) {
    System.out.println("Deployment id is: " + res.result());
  } else {
    System.out.println("Deployment failed!");
  }
})
  • 如果部署成功,这个完成处理器的结果中将会包含部署ID的字符串。
  • 这个部署ID可以用于撤销部署。

撤销

vertx.undeploy(deploymentID, res -> {
  if (res.succeeded()) {
    System.out.println("Undeployed ok");
  } else {
    System.out.println("Undeploy failed!");
  }
});

设置 Verticle 实例数量

使用名称部署 Verticle 时,可以指定需要部署的 Verticle 实例的数量。

DeploymentOptions options = new DeploymentOptions().setInstances(16);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);

这个功能对于跨多核扩展时很有用。例如,您有一个带Web服务的Verticle需要部署在多核的机器上, 您可以部署多个实例来利用所有的核。

向 Verticle 传入配置

可在部署时传给 Verticle 一个 JSON 格式的配置

JsonObject config = new JsonObject().put("name", "tim").put("directory", "/blah");
DeploymentOptions options = new DeploymentOptions().setConfig(config);
vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);

传入之后,这个配置可以通过 Context对象或使用 config方法访问。这个配置会以 JSON 对象(JsonObject)的形式返回, 因此您可以用下边代码读取数据:

System.out.println("Configuration: " + config().getString("name"));

退出 Vert.x 环境

Vert.x 实例维护的线程不是守护线程,因此它们会阻止JVM退出。
如果您通过嵌入式的方式使用 Vert.x 并且完成了操作,您可以调用 close方法关闭它。
这将关闭所有内部线程池并关闭其他资源,允许JVM退出。

Context 对象

当 Vert.x 传递一个事件给处理器或者调用 Verticle 的 start 或 stop 方法时, 它会关联一个 Context 对象来执行。通常来说这个context会是一个 event-loop context,它绑定到了一个特定的 Event Loop 线程上。所以在该context上执行的操作总是 在同一个 Event Loop 线程中。对于运行内联的阻塞代码的 Worker Verticle 来说,会关联一个 Worker Context,并且所有的操作运都会运行在 Worker 线程池的线程上。(译者注:每个 Verticle 在部署的时候都会被分配一个 Context(根据配置不同,可以是Event Loop Context 或者 Worker Context),之后此 Verticle 上所有的普通代码都会在此 Context 上执行(即对应的 Event Loop 或Worker 线程)。一个 Context 对应一个 Event Loop 线程(或 Worker 线程),但一个 Event Loop 可能对应多个 Context。)
您可以通过 getOrCreateContext 方法获取 Context 实例:

Context context = vertx.getOrCreateContext();

若已经有一个context和当前线程关联,那么它直接重用这个context对象, 如果没有则创建一个新的。您可以检查获取的context的 类型 :

Context context = vertx.getOrCreateContext();
if (context.isEventLoopContext()) {
  System.out.println("Context attached to Event Loop");
} else if (context.isWorkerContext()) {
  System.out.println("Context attached to Worker Thread");
} else if (! Context.isOnVertxThread()) {
  System.out.println("Context not attached to a thread managed by vert.x");
}

当您获取了这个context对象,您就可以在context中异步执行代码了。换句话说, 您提交的任务将会在同一个context中运行:

vertx.getOrCreateContext().runOnContext( (v) -> {
  System.out.println("This will be executed asynchronously in the same context");
});

当在同一个context中运行了多个处理函数时,可能需要在它们之间共享数据。 context对象提供了存储和读取共享数据的方法。举例来说,它允许您将数据传递到 runOnContext方法运行的某些操作中:

final Context context = vertx.getOrCreateContext();
context.put("data", "hello");
context.runOnContext((v) -> {
String hello = context.get("data");
});

执行周期性/延迟性操作

  • 在 Vert.x 中,延迟执行或定期执行操作很常见。
  • 在 Standard Verticle 中您不能直接让线程休眠以引入延迟,因为它会阻塞 Event Loop 线程。
  • 取而代之是使用 Vert.x 定时器。定时器可以是 一次性 或 周期性 的,两者我们都会讨论到。

一次性计时器

  • 一次性计时器会在一定延迟后调用一个 Event Handler,以毫秒为单位计时。
  • 您可以通过 setTimer方法传递延迟时间和一个处理器来设置计时器的触发。
long timerID = vertx.setTimer(1000, id -> {
  System.out.println("And one second later this is printed");
});
System.out.println("First this is printed");

返回值是一个唯一的计时器id,该id可用于之后取消该计时器,这个计时器id会传入给处理器。

周期性计时器

  • 您同样可以使用 setPeriodic方法设置一个周期性触发的计时器。
  • 第一次触发之前同样会有一段设置的延时时间。
  • setPeriodic 方法的返回值也是一个唯一的计时器id,若之后该计时器需要取消则使用该id。传给处理器的参数也是这个唯一的计时器id。
  • 请记住这个计时器将会定期触发。如果您的定时任务会花费大量的时间,则您的计时器事件可能会连续执行, 甚至发生更坏的情况:重叠。
  • 这种情况,您应考虑使用 setTimer方法, 当任务执行完成时设置下一个计时器。
long timerIds = vertx.setPeriodic(1000, ids -> {
      long timerId = vertx.setTimer(1000, id -> {
        System.out.println("And one second later this is printed");
      });
    });
    System.out.println("First this is printed");

取消计时器

vertx.cancelTimer(timerID);
  • 另外如果您在 Verticle 中创建了计时器, 当这个 Verticle 被撤销时这个计时器会被自动关闭。

转载自 vertx 中文翻译文档