您的位置:首页 > 其它

Vert.x 核心模块 内核组件Verticle(二)

2016-09-13 09:10 309 查看
Verticles

Vert.x俱有简单,伸缩,actor-like部署和并发模型的创新,这样可以省去自己去实现。

 这个模型是完全可选的,Vert.x不强迫你使用这种方式创建应用,如果你不想那样做。

 此模型没有被申明为严格的actor-model实现,但是它在并发,伸缩和部署方面都特别相似。

为了使用此模型,你编写verticle集合的代码。

Verticle是一个代码块,此代码块由Vert.x部署和运行。一个Vert.x实例维护N个事件循环线程(默认N为CPU核数*2)。Verticles可以使用Vert.x支持的任一语言编写,并且单一应用可以包含用多种语言编写的verticles.

 可以将verticles想成一个Actor模型中的一个actor.

 一个应用可能是这样一个典型组合,在同一时间同一Vert.x实例中运行多个verticles。不同的verticle实例通过向事件总线发送信息相互通迅。

 编写Verticles

Verticle类必须实现Verticle接口。如果你喜欢你可以直接实现Verticle接口,但是通常是简单扩展抽象类AbstractVerticle.

这是一个verticle例子

public class MyVerticle extendsAbstractVerticle {

  //Called when verticle is deployed

 public void start() {

  }

  //Optional - called when verticle is undeployed

 public void stop() {

  }

}

正常情况,你上面例子,你将覆盖start方法。当Vert.x布署Verticles时,Vert.x将调用start方法,并且当此方法结束时,Veticle将被认为启动成功。

你可以选择性重写stop方法。这将被Vert.x调用,在verticle被御载时,stop方法完成执行,Verticle将被认为停止。

异步Vertical开始与停止

有时你想在你的Verticle启动的时候做一些费时的事情,并且不想这个verticle已经部署成功。例如,你想在start方法中部署其他veticles

 你不能阻塞等待其他verticles在你的verticle的start方法中进行部署,因为这将打破黄金法则

如果这样,我们应怎样做呢?

方法是实现异步的start方法.此方法的这个版本用一个future作为参数。当这个方法返回时,此Veticle不会被认为部署完成。

在你做完你需要做的工作之后过一会(比如开始其他verticles),在完成或失败的future上调用完成通知你已经做完。

这儿是一个例子

public class MyVerticle extendsAbstractVerticle {

 public void start(Future<Void> startFuture) {

   // Now deploy some other verticle:

   vertx.deployVerticle("com.foo.OtherVerticle", res -> {

     if (res.succeeded()) {

       startFuture.complete();

     } else {

       startFuture.fail(res.cause());

     }

   });

  }

}

相同的,也有一个异步版本的stop方法。如果你想做一些耗时的Verticle清除工作可以使用它。

public void start() {

   // Do something

  }

  public void stop(Future<Void> stopFuture) {

   obj.doSomethingThatTakesTime(res -> {

     if (res.succeeded()) {

       stopFuture.complete();

     } else {

       stopFuture.fail();

     }

   });

  }

信息:在verticle的stop方法中,你不必手工操作御载子verticle,这些子verticle由某个verticle启动。Vert.x在你父verticle御载时自动御载子verticles.

 

Verticle类型

有三个不同类型的Verticles:

标准Verticles:

这种类型是常用并且有用的类型,他们总是通过事件循不被执行。在下一节我们将对此讨论更多。

WorkerVerticles

此类型的verticle通过从workerpool中取出线程执行。一个verticle实例从不被多个线程并发执行(由单一线程执行)

多线程workerVerticles

此类Verticles从Workerpool中取得线程执行,一个verticle实例可被多个线程并发执行.

 标准verticles

当标准Verticles在创建且start方法被事件循不调用,将被赋给一个事件循环线程。当你从事件循环中使用内核APIs调用持有处理器的方法时,Vert.x将保证这些处理器在同一个事件循环中被执行。

这意味着,我们能保证你的Verticles实例中的所有代码,将在同一个事件循环中执行(只要你不创建自己的线程并调用它)。

 这意味着,你可以在你的应用中编写像单线程的代码,并且让Vert.x关心线程和伸缩。不需要关心异步和多变,并且你避免一些竞争条件和列锁,所以能熟练进行传统手工多线程应用开发。

 Workerverticles

 一个Work verticles类似于标准verticle,但是不是由事件循环执行,而是从Vert.x 工作线程池中获取线程。

Worker verticles被设计于调用阻塞代码。因为他们不阻塞任何事件循环。

如果你不想使用Worker verticle运行阻塞代码,你也可以直接在一个事件循环中执行内联阻塞代码。

 如果你想布署一个Worker verticle 你可以使用setWorker方法完成

DeploymentOptions options = newDeploymentOptions().setWorker(true);

vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle",options);

 Worker verticle 实例从不被Vert.x并发多线程执行,但是可以被不同的线程在不同的时间执行。

多线程worker verticles

一个多线程worker verticle与普通的worker verticle类似,除了它可以并发被多线程执行。

警示:多线程worker verticle是一个高级特性,大多数应用并不需要。因为在这此verticles中并发运行,你必须非常小心保持verticle处于一致状态,正如多线程编程中标准的java技术。

程序化布署verticles

可以使用deployVerticle方法中的任一个布署Verticle,你可以指定一个verticle名称或者,传入一个已经创建好的verticle实例。

备注:仅有Java可以布署Verticle实例。

Verticle myVerticle = new MyVerticle();

vertx.deployVerticle(myVerticle);

 可以指定Verticle名称进行布署。

Verticle名称被用于查找特定的用于初始化实际Verticle实例的VerticleFactory。不同的Verticle工厂用于不同的语言初始化不同的Verticle实例,也用于其他原因如装载服务,在运行时从Maven获取verticles. 这就允许布署Vert.x支持的语言编写的verticle.

下面是一个布署不同类型verticle例子:

vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle");

// Deploy a JavaScript verticle

vertx.deployVerticle("verticles/myverticle.js");

// Deploy a Ruby verticle verticle

vertx.deployVerticle("verticles/my_verticle.rb");

 映射verticle名称到verticle工厂的规则

当用名称布署verticle(s)时,名称用于选择一个初始化verticle(s)的verticle工厂。Verticle名称可以有个前缀一个后面带冒号字符串,如果存在则用于寻找verticle工厂,例如:

js:foo.js // Use the JavaScript verticlefactory

groovy:com.mycompany.SomeGroovyCompiledVerticle// Use the Groovy verticle factory

service:com.mycompany:myorderservice //Uses the service verticle factory

如果存在前缀,Vert.x将会查找一个后缀并且用于查找verticle工厂,例如:

foo.js // Will also use the JavaScriptverticle factory

SomeScript.groovy // Will use the Groovyverticle factory

如果没有前缀或者后缀,Vert.x将假设名称是一个全java类路径并且偿试初始化。

 怎样定位Verticle工厂?

大多数Verticle工厂是在Vert.x启动时从类路径中加载并注册。也可以用程序的方式注册或者解注册Verticle,这需要调用registerVerticleFactory和unregisterVerticleFactory两个方法。

等待布署完成

Verticle布署是异布的 并且可以在调用布署返回之后需要一些时间完成布署。如果你想在Verticle布署完成之后得到通知,可以在布署时指定一个处理器:

vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle",res -> {

  if (res.succeeded()) {

    System.out.println("Deployment id is:" + res.result());

  } else {

    System.out.println("Deploymentfailed!");

  }

});

如果布署完成,完成处理器将会得到一个包含布署ID的字符串结果。布署ID在稍后御载verticle方法中会用到。

御载Verticle

可以使用undeploy方法进行verticle御载。御载本身是异步的,因此,如果你想得到御载完成的通知,你可以指定一个完成处理器:

vertx.undeploy(deploymentID, res -> {

  if(res.succeeded()) {

   System.out.println("Undeployed ok");

  }else {

   System.out.println("Undeploy failed!");

  }

});

指定Verticle实例数量

当用Verticle名称布署verticle时,你根据需要可以指定verticle实例的数量:

DeploymentOptions options = newDeploymentOptions().setInstances(16);

vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle",options);

这为方便伸缩到不同的CPU运算内核是有用的。例如,你可能有一个web-server的Verticle布署到多核的机器上,所以你想布署多个verticle实例,充份利用所有CPU运算核心。

向verticle传递配置

JSON格式的配置可以在布署时传给verticle:

JsonObject config = newJsonObject().put("name", "tim").put("directory","/blah");

DeploymentOptions options = newDeploymentOptions().setConfig(config);

vertx.deployVerticle("com.mycompany.MyOrderProcessorVerticle",options);

然后可能通过Contex对象或者直接使用config方法得到这个配置。因为配置以JSON对向被返回,所以可用下面的代面进行检查:

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

在Verticle中访问环境变量

使用Java API可以访问环境变量和系统属性:

System.getProperty("prop");

System.getenv("HOME");

Verticle隔离组

默认Vert.x有一个扁平的类路径(classpath).例如,当Vert.x布署verticles时使用当前的类加载器(classloader),Vert.x并不创建一个新的类加载器。在大部分情况下,这是在做很简单,很清晰,明智的的事。然而,在有些情况下,你也许想在应用中布署一个隔离于其他verticles的verticle。这是或许是一种情况,例如,你想在同一个Vert.x实例中布署一个具有相同名称两个不同版的verticle,或者你有两个不同的verticle,它们都使用了同一个jar包的不同版本。

在使用隔离组时,你提供一个需要隔离的类名的列表作为setIsolatedClasses方法的参数,类名必符是有效全路径类名,如com.mycompany.myproject.engine.MyClass或者是一个通配符用于匹配本包或者子包内任何类,如,com.mycompany.myproject.*,会匹配com.mycompany.myproject包或子包中的任意类。注意只有匹配的类将全隔离,其他的类将有当前类加载器加载。外部类可以通过setExtraClasspath方法提供,所以你可以添加需要加载的当前不在主类路径中的类和资源。

警示:小心使用此特性。类加载器是系统的bug瓶子,而且在其他东西上,让调试团难。

这是一个使用隔离组隔离verticle的例子:

DeploymentOptionsoptions = new DeploymentOptions().setIsolationGroup("mygroup");

options.setIsolatedClasses(Arrays.asList("com.mycompany.myverticle.*",

                  "com.mycompany.somepkg.SomeClass","org.somelibrary.*"));

vertx.deployVerticle("com.mycompany.myverticle.VerticleClass",options);

高可用性

Verticle可以进行高可用布署。在那个上下文中,一个部署前瞻verticle的vert.x实例突然死亡,这个verticle会重新布署到这个群集中的其他Vert.x实例上。为了运行verticle高可用,添加-ha开关项:

vertx run my-verticle.js –ha

在使用可高用时,不需要添加-cluster参数。关于更多的高可用特性及设置可参见高可用和容错小节。

从命令行运行Verticles

可以通过直接在maven或Gradle项目中添加Vert.x 核心库的正常方式使它。但是也可直接从命令行运行Vert.x的verticle. 这样做,需要下载并安装Vert.x分发版,并且将bin路径添加到你的PATH环境变量中。并确认JDK8可执行。

注意:需要JDK支持自由编译Java代码。

现在你可以用vertx run命令运行verticle了。下面是一些例子:

# Run a JavaScript verticle

vertx run my_verticle.js

 

# Run a Ruby verticle

vertx run a_n_other_verticle.rb

 

# Run a Groovy script verticle, clustered

vertx run FooVerticle.groovy -cluster

 

你甚至于不用编译就运行Java源代码的Verticle;

Vertx run SomeJavaSourceFile.java

Vert.x将在运行时编译源代码然后运行。这对于快速原型设计verticlet和演示确实有用。不需要在运行前安装Maven或者Gradle。

 导至Vert.x退出

Vert.x实例维护着线程,而非精灵线程,所以他们将阻止JVM退出。如果你正在嵌入Vert.x并且已经使用完,可以调用call方法关闭它。这会关闭所有内部线程池及所有其他资源,并允许JVM退出。

上下文(Context)对象

在Vert.x向一个处理器提供事件或者调用某个Verticle的start/stop方法时,此次执行将与上下文关联。通常上下文指的是事件循环上下文并且与特定的事件循环线程绑定。如果是worker verticle并且运行内联阻塞代码,一个worker上下文与此操作关联。Worker上下文来自于worker线程池。

使用getOrCreateContext方法得到上下文:

Context context =vertx.getOrCreateContext();

如果当前线程已经关联到某个上下文,它将重要此上下文对象。如果不是创建一个新的上下文实例,可以检测你获取的上下文类型:

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.isMultiThreadedWorkerContext()) {

 System.out.println("Context attached to Worker Thread - multithreaded worker");

} else if (! Context.isOnVertxThread()) {

 System.out.println("Context not attached to a thread managed byvert.x");

}

在收到上下文对象时,可以异步在此上下文中执行代码。换句话说,就是可以提交一个稍后将执行的任务到相同的上下文中。:

vertx.getOrCreateContext().runOnContext((v) -> {

 System.out.println("This will be executed asynchronously in thesame context");

});

当在同一个上下文中运行多个处理器时,处理器间可以共享数据。上下文对象提供了存取共享数据的方法。例如,通过runOnContext方法将数据传到某个行为。

final Context context = vertx.getOrCreateContext();

context.put("data","hello");

context.runOnContext((v) -> {

 String hello = context.get("data");

});

通过上下文对象的config方法,也可访问verticle的配置。查看传递配置到verticle小节获取更多细节。

 执行周期性和延迟性动作

在Vert.x中执行延迟任务和周期任务是很常见的。在标准verticle中,仅需要使线程暂停就则引入延迟,因为可以阻塞事件循环线程。另外可以使用Vert.x定时器,定时器可以是一次性的或者周期性的。我们将对二者进行讨论。

一次性定时器

一次性定时器在特定的延迟后调用一个事件处理器,定时用毫秒表示。使用setTimer方法传入一个延迟时间和一个处理器设置一次性处理器。

long timerID = vertx.setTimer(1000, id-> {

 System.out.println("And one second later this is printed");

});

System.out.println("First this isprinted");

周期定时器

使用setPeriodic方法设置一个周期触发的定时器。初始化延迟与周期相同。setPeriodic方法返回值是一个定时器ID(long),这个ID在定时器取消时要用到。唯一的定时器ID也会作为参数传给定时器事件处理器。记住定时器将基于周期性触发。如果周期任务处理将消耗很长时间,定时器事件将持续运行,最差的情况是定时器事件会累加。在这种情况下,最好用setTimer来代替。一旦作任务完成,你可以设置下一个定时器。

long timerID = vertx.setPeriodic(1000, id-> {

 System.out.println("And every second this is printed");

});

System.out.println("First this isprinted");

取消定时器

为了取消周期定时器,可以调用cancelTimer方法,并传入定时器ID。例如

vertx.cancelTimer(timerID);

Verticle中自动清除定时器

如果你在Verticles内部创建定时器,这些定时器将在Verticle御载时自动关闭。

Verticle工作池

Verticle用Vert.x工作线程池执行阻塞动作,如executeBlocking或者worker verticle. 可以在在布署verticle指定一个工作线程池。

vertx.deployVerticle("the-verticle",new DeploymentOptions().setWorkerPoolName("the-specific-pool"));
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Vert.x verticle acto