Verticles

Vert.x引入了一个简单的可扩展的类actor的部署和并发模型.

这个模型是可选的, 如果你不想要采取该模型也可以不实现它,vertx并不强制要求你实现它.

这个模型并不是actor-model的严格实现, 但是该模型在并发处理,拓展模式和开发模式上确实和actor-model非常像。

其实当我们在verticle中开始实现逻辑代码时,就已经开始使用这个开发模型了。

verticle简而言之就是一个代码块,然后你通过Vert.x部署和运行它. 我们可以使用Vert.x支持的不同语言实现verticle,而且一个单独的应用程序中可以包含多种语言实现的verticle.

你可以把verticle理解成Actor模型中的actor.

一般来说,一个Vert.x应用应该只是由verticle实例构成.不同的 verticle可以在event bus上通过发送消息进行交互.

Writing Verticles

Javaverticle必须实现Verticle接口。

当然如果你不想实现这个接口,还有一种其他定义方法,那就是继承AbstractVerticle抽象类

  1. public class MyVerticle extends AbstractVerticle {
  2. // Called when verticle is deployed
  3. public void start() {
  4. }
  5. // Optional - called when verticle is undeployed
  6. public void stop() {
  7. }
  8. }

verticle在被Vert.x部署的过程中,verticlestart方法会被调用,当start方法被调用完之后,verticle就被认为部署完成.

verticle实现中像例子中start方法是必须要实现的,但是stop方法可以选择不实现. stop方法是当verticleundeployed进行调用的,当stop方法调用完成之后,verticle就被认为停止运行了

Asynchronous Verticle start and stop

有时你想要在verticle开始部署阶段(start方法中)完成一些耗时的操作,但是当这些操作完成之前,你不希望当前verticle处于部署完成状态.例如你想要在start方法中部署其他verticle,但是由于部署是异步进行的,因此可能主verticle都已经返回了,但是其他verticle的部署工作还没有完成,那么你就不想让主verticle处于完成状态.

但是你也不能在start方法中进行阻塞等待其他verticle部署完成,在event loop无论何时你都不应该把它阻塞掉。

那么该怎么办呢?我们的办法是你实现一个asynchronousstart方法,这个方法会传入一个Future对象作为参数.即使当start方法返回了,该verticle也不会被认为已经完成部署了。

当你在start方法里所有的工作都完成之后,通过调用Future对象的complete方法,在外部获得一个通知,部署工作真正完成了.

  1. public class MyVerticle extends AbstractVerticle {
  2. public void start(Future<Void> startFuture) {
  3. // Now deploy some other verticle:
  4. vertx.deployVerticle("com.foo.OtherVerticle", res -> {
  5. if (res.succeeded()) {
  6. startFuture.complete();
  7. } else {
  8. startFuture.fail();
  9. }
  10. });
  11. }
  12. }

同样的,stop方法也有一个asynchronous版本的.

  1. public class MyVerticle extends AbstractVerticle {
  2. public void start() {
  3. // Do something
  4. }
  5. public void stop(Future<Void> startFuture) {
  6. obj.doSomethingThatTakesTime(res -> {
  7. if (res.succeeded()) {
  8. startFuture.complete();
  9. } else {
  10. startFuture.fail();
  11. }
  12. });
  13. }
  14. }

注意,通过verticle部署的vertcle,这俩种vertcle会构成一种”父子”关系,当父verticleundeploy后,子verticle会自动被Vert.x进行undeploy

Verticle Types

在Vert.x中有三种不同类型的verticle

  • Standard Verticles : 这是最常用的一种. 这种verticle通过event loop线程执行.接下来我们会详细讨论这种verticle.
  • Worker Verticles : 这种verticle通过worker pool中的线程执行。该verticle在同一时刻永远不会被多个线程并发执行
  • Multi-threaded worker verticles : 该verticle同样通过worker pool中的线程执行.但是这种verticle可能会被多个线程并发执行.

Standard verticles

Standard Verticles当被创建的时候会被分配到一个event loop上, 同时Standard Verticlesstart()会被该event loop进行调用. 当你在event loop中,通过核心API以及带有handler参数的的方式调用其他方法时,Vert.x确保那些handler回调时是被刚才那个event loop进行调用的.

这意味着,我们能保证vertcle实例里全部代码总是能被相同的event loop进行调用(当然这是在你不故意自己创建线程调用他们的前提下).

这意味着,当你基于Vert.x开发应用程序时,vert.x会帮你完成那些并发操作,你自己完全不需要考虑多线程和并发情况,只需要像在单线程中那样写代码就好了. 从此你的生活就远离了synchronized, volatile, 条件竞争, 死锁等等..

Worker verticles

worker verticlestandard verticle相比,worker verticle并不是运行在event loop中,而是在worker thread pool中的某个线程中运行.

worker verticle是被设计成专门用来调用阻塞代码的,他们不会阻塞掉任何的event loop.

如果你不想在worker verticle中运行阻塞代码, 你也可以在event loop中执行运行内联的阻塞代码.

如果你想要部署worker verticle时, 你可以使用setWorker().

  1. DeploymentOptions options = new DeploymentOptions().setWorker(true);
  2. vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", options);

Worker verticle实例永远不会被多线程同一时间并发执行,但是却可以在不同的时间被不同的线程执行.

Multi-threaded worker verticles

multi-threaded worker verticleworker verticle很像,只不过这种multi-threaded worker verticle可以被多个不同线程并发执行.

注意:multi-threaded worker verticle是一个非常高级的特性,而且大部分的应用程序并不会需要使用到它. 因为在这种verticle中的并发操作,你需要非常小心通过使用传统的多线程编程技术保持verticle的状态一致性.

Deploying verticles programmatically

你可以通过deployVerticle()方法部署一个verticle, 使用这种方式你需要指定该verticle的名字或者传递一个你已经创建好的该verticle的实例.

注意: 只有在java中才可以部署Verticle实例

  1. Verticle myVerticle = new MyVerticle();
  2. vertx.deployVerticle(myVerticle);

verticle名字用来查找特定的VerticleFactory, 我们使用VerticleFactory来实例化出实际的verticle实例.

不同的VerticleFactory用于在不同的语言实现中对verticle进行实例化, 除此之外不同的VerticleFactory也用于加载service或者在Maven运行时获得verticle.

这种特性可以让你在某种语言中部署其他语言实现的verticle.

下面的例子演示了在Java中部署不同语言类型的verticle

  1. vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle");
  2. // Deploy a JavaScript verticle
  3. vertx.deployVerticle("verticles/myverticle.js");
  4. // Deploy a Ruby verticle verticle
  5. vertx.deployVerticle("verticles/my_verticle.rb");

Rules for mapping a verticle name to a verticle factory

当使用verticle名称部署verticle时,这个名字被用来找到实际的VerticleFactory,对verticle进行实例化

verticle名称还可以有一个前缀(随后跟一个冒号),当该前缀被指定后,会使用该前缀来查找VerticleFactory.

  1. js:foo.js // Use the JavaScript verticle factory
  2. groovy:com.mycompany.SomeGroovyCompiledVerticle // Use the Groovy verticle factory
  3. service:com.mycompany:myorderservice // Uses the service verticle factory

如果我们并没有指定前缀,那么Vert.x会去名称中找到后缀(文件类型),然后使用该后缀找到VerticleFactory.

  1. foo.js // Will also use the JavaScript verticle factory
  2. SomeScript.groovy // Will use the Groovy verticle factory

但是如果既没有前缀,也没有后缀被找到,那么Vert.x会认为这个名称是个Java类的全限定名,使用使用这个全限定名进行实例化

How are Verticle Factories located?

在Vert.x启动时,它会在classpath中对大多数的VerticleFactory加载和注册.

当然如果你想在程序中通过编程的方式对VerticleFactory进行注册和解除注册到话,你可以使用registerVerticleFactoryunregisterVerticleFactory方法

Waiting for deployment to complete

同样verticle的部署也是异步进行的, 当调用部署方法进行返回的时候也许部署操作并没有真正的,可能要等到过一段时间才能真正完成,

如果你想当部署操作真正完成的时候捕获一个通知,你可以在部署方法里添加一个completion handler,用于处理完成时候你想进行的特定操作.

  1. vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle", res -> {
  2. if (res.succeeded()) {
  3. System.out.println("Deployment id is: " + res.result());
  4. } else {
  5. System.out.println("Deployment failed!");
  6. }
  7. });

如果部署成功了,handler会捕获一个result(内含一个字符串形式的部署ID).

当你后期想要对verticle进行undeploy操作时,你就需要使用刚才的那个部署成功时获得的字符串形式的部署ID了.

Undeploying verticle deployments

verticle被部署成功之后,我们也可以通过调用undeploy方法对其进行undeploy操作.

当然undeploy一样是异步进行的,如果你想当undeploy操作完成时同样捕获通知,你也可以对undeploy方法设置一个completion handler.

  1. vertx.undeploy(deploymentID, res -> {
  2. if (res.succeeded()) {
  3. System.out.println("Undeployed ok");
  4. } else {
  5. System.out.println("Undeploy failed!");
  6. }
  7. });

Specifying number of verticle instances

当你使用verticle名字对某个verticle进行部署时,你也可以指定该verticle部署成功后的实例数量:

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

当我们想在多核主机上对应用进行拓展时,通过这种方式就可以轻松实现了. 假设,你现在要在一个多核主机上部署一个web-server verticle,因此你想要对该verticle部署多个实例以便能使用上所有核心.

Passing configuration to a verticle

verticle被部署时,我们还可以向其指定一个JSON形式的配置:

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

稍后我们就可以通过Context对象来操作Configuration配置了

TODO

Accessing environment variables in a Verticle

TODO

Verticle Isolation Groups

By default, Vert.x has a flat classpath. I.e, it does everything, including deploying verticles without messing with class-loaders. In the majority of cases this is the simplest, clearest and sanest thing to do.

Vert.x默认有一个flat classpath,它会实现N多功能,包括在部署verticle时不会干扰类加载的工作. 在大多数情况下,这是最简单,清晰,明智的事情。

However, in some cases you may want to deploy a verticle so the classes of that verticle are isolated from others in your application.

This might be the case, for example, if you want to deploy two different versions of a verticle with the same class name in the same Vert.x instance, or if you have two different verticles which use different versions of the same jar library.

WARNING Use this feature with caution. Class-loaders can be a can of worms, and can make debugging difficult, amongst other things. Here’s an example of using an isolation group to isolate a verticle deployment.

  1. DeploymentOptions options = new DeploymentOptions().setIsolationGroup("mygroup");
  2. options.setExtraClasspath(Arrays.asList("lib/jars/some-library.jar"));
  3. vertx.deployVerticle("com.mycompany.MyIsolatedVerticle", options);

Isolation groups are identified by a name, and the name can be used between different deployments if you want them to share an isolated class-loader.

Extra classpath entries can also be provided with setExtraClasspath so they can locate resources that are isolated to them.

High Availability

Verticles can be deployed with High Availability (HA) enabled.

TODO

Running Verticles from the command line

通常做法是你可以在Maven或者Gradle项目中添加一个Vert.x core library引用,你就可以直接运行Vert.x了.

然而,你如果不习惯那种做法,你还可以直接在命令行中执行运行Vert.x verticle.

在命令行中运行Vert.x你需要下载Vert.x的分发版本,然后将安装好的bin目录添加到Path环境变量中,同时要确保在PATH中你也添加上了JDK8.

下例演示了如何直接在命令中运行verticle

  1. # Run a JavaScript verticle
  2. vertx run my_verticle.js
  3. # Run a Ruby verticle
  4. vertx run a_n_other_verticle.rb
  5. # Run a Groovy script verticle, clustered
  6. vertx run FooVerticle.groovy -cluster

令人惊喜的是,你可以在命令行中直接运行java源文件.

  1. vertx run SomeJavaSourceFile.java

Vert.x会在运行该java源文件之前自己去编译它. 这对于quickly prototyping verticle和写verticle demo是非常有用的.

Causing Vert.x to exit

Threads maintained by Vert.x instances are not daemon threads so they will prevent the JVM from exiting.

如果你将Vert.x嵌入在了你的应用程序中,而且当你的应用程序已经使用完了Vert.x的功能,你需要关闭掉Vert.x的时候,你可以直接调用close将其关闭掉.

这个操作会关闭Vert.x内部所有的线程池和其他的资源,但是并不会让JVM也跟着关闭掉.

The Context object

TODO

Executing periodic and delayed actions

It’s very common in Vert.x to want to perform an action after a delay, or periodically.

standard verticle中,你不能因为想要得到一个延迟的效果就将线程sleep掉,因为这个操作会将event loop线程阻塞掉.

取而代之的是,你可以使用Vert.x timers,Vert.x timers既可以执行一次也可以周期性执行.

One-shot Timers

one shot timer会在一个特定的延迟后(单位毫秒)调用一个event handler.

设置one shot timer是非常简单的,调用setTimer方法然后设置一个延迟和一个handler就ok了.

  1. long timerID = vertx.setTimer(1000, id -> {
  2. System.out.println("And one second later this is printed");
  3. });
  4. System.out.println("First this is printed");

这个返回的返回值是一个唯一的timer id,如果你想在后期取消掉这个timer,就需要使用这个id了.

Periodic Timers

你可以通过调用setPeriodic方法设置一个周期性的定时器.

There will be an initial delay equal to the period.

The return value of setPeriodic is a unique timer id (long). This can be later used if the timer needs to be cancelled.

The argument passed into the timer event handler is also the unique timer id:

  1. long timerID = vertx.setPeriodic(1000, id -> {
  2. System.out.println("And every second this is printed");
  3. });
  4. System.out.println("First this is printed");

Cancelling timers

To cancel a periodic timer, call cancelTimer specifying the timer id. For example:

  1. vertx.cancelTimer(timerID);

Automatic clean-up in verticles

If you’re creating timers from inside verticles, those timers will be automatically closed when the verticle is undeployed.