[Vert.x Core手册 for Java]-了解Vert.x

源码在github

Vert.x Core提供的功能:

  • 编写TCP客户端和服务器
  • 编写 HTTP 客户端和服务器包括 Websocket 支持
  • 事件总线(Event bus)
  • 共享的数据-本地的map和分布式的map
  • 定时和延时运行
  • 部署和非部署 Verticles
  • Sockets
  • DNS 客户端
  • 文件系统
  • 高可用性
  • 集群

Vert.x核心功能是相当简单的 — — 你不会找到数据库访问、 授权或高级别 web 功能等,这些东西你可以在哪里找到?在这里-,Vert.x ext(扩展)。

Vert.x core 非常小,非常轻量级。只是使用你想要的部分。也是完全可嵌入在您现有的应用程序 — — 不强迫你使用特殊方式架构您的应用程序,这样你可以方向使用 Vert.x。

您可以使用任何 Vert.x 支持的其他语言的核心。这有点小酷-我们不强迫你使用 Java API ,JavaScript 或者 Ruby等都没问题 — — 毕竟,不同的语言有不同的习惯和语法,迫使Ruby 开发人员使用 Java 的语法,这会很奇怪 (举个例子)。相反,我们自动生成以 Java Api 为核心,等效、地道的每种语言。

从现在起我们会使用 core 指 Vert.x core。

如果你使用 Maven 或 Gradle,需要增加以下依赖才能使用Vert.x Core API:

  • Maven (在你的pom.xml中):
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-core</artifactId>
  <version>3.2.0</version>
</dependency>
  • Gradle (在您的build.gradle文件):
compile io.vertx:vertx-core:3.2.0

下面让我们来讨论 core 的不同概念和功能。

从Vert.x开始

注意:这大部分是Java特有的-需要语言特有的调用方法

如果没有获得Vertx对象,Vert.x做不了什么。

Vertx对象是 Vert.x 的控制中心,几乎可以做所有事,包括创建客户端和服务器,获取引用到事件总线(event bus)、 设置计时器等。

所以怎么获得Vertx实例?

如果已经嵌入了 Vert.x,然后只需创建一个实例,如下所示:

Vertx vertx = Vertx.vertx();

如果使用 Verticles

注意:大多数应用程序只需要一个单一的 Vert.x 实例,但如果你需要,可以创建多个 Vert.x 实例,例如,事件总线或不同的服务器和客户端之间的隔离。

创建一个指定选项的Vertx 对象

创建一个 Vertx 对象时,如果默认值不是正确的选择,你还可以指定选项:

Vertx vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(40));

VertxOptions对象有许多设置,可以配置集群、 高可用性、 池的大小等。所有设置细节在Javadoc 中有描述。

创建群集 Vert.x 对象

如果您正在创建clustered(群集) Vert.x (更多集群相关的请参阅事件总线( event bus)),然后通常会使用异步方式创建 Vertx 对象。

这是因为不同的 Vert.x 实例在群集中组合在一起,通常需要一些时间 (也许几秒钟) 的。在这段时间,我们不想阻止调用线程,所以我们把结果以异步方式给你。

你是傻瓜吗?

你可能注意到,在前面使用fluent API(fluent API:流API,更易使用的API,也称傻瓜式API)的例子。

fluent API 是支持链式调用的。例如:

request.response().putHeader("Content-Type", "text/plain").write("some text").end();

整个 Vert.x Api都是这种模式,,所以要去适应它。

可以链式编写代码,当然你也可以按自己的喜欢,写上这样的代码:

HttpServerResponse response = request.response();
response.putHeader("Content-Type", "text/plain");
response.write("some text");
response.end();

不要call(调用、打电话)我们,我们会call给你。

Vert.x Api 是很大程度上由事件驱动的。这意味着,当事情发生在你感兴趣的Vert.x,Vert.x 会通过回调方式向您发送events。

一些示例events:

  • 计时器激活
  • socket收到数据
  • 从磁盘读取数据
  • 发生了异常
  • HTTP 服务器收到请求

通过向 Vert.x Api 提供处理程序来处理事件。例如要接收一个计时器事件每一秒你会做:

vertx.setPeriodic(1000, id -> {
  // This handler will get called every second
  System.out.println("timer fired!");
});

或接收到 HTTP 请求:

server.requestHandler(request -> {
  // This handler will be called every time an HTTP request is received at the server
  request.response().end("hello world!");
});

一段时间后当 Vert.x 有一个事件,它将传递到您的处理程序 Vert.x 将它异步调用.

这将引导我们进入Vert.x 中的一些重要概念:

不要阻塞我!

除了极少数例外 (一些文件系统操作的“同步”结束),没有一个 Vert.x Api 阻塞调用线程。

如果可以立即提供的结果,它将立即返回,你通常会提供一个handle来接收过一段时间的事件。

由于Vert.x API没有任何阻塞的线程,这意味着你可以使用Vert.x来处理只是使用小数目线程的大量并发。

常规阻塞API使用线程可能会阻塞:

  • 从socket读取数据
  • 向磁盘写入数据
  • 向收件人发送一条消息,等待答复。

在所有上述情况下,当您的线程正在等待结果时它不能做别的-这是实际上是浪费。

这意味着,如果你需要大量的并发使用阻塞 APIs,然后你需要大量的线程,以防止您的应用程序停止工作。

线程在他们所需要的内存(例如栈)和上下文切换方面有开销。

对于许多现代应用程序所需要的并发水平,阻塞的方法不能按比例缩放。

Reactor和多Reactor

之前提到Vert.x API是事件驱动 - 当他们都可用时,Vert.x传递事件给处理程序。

在大多数情况下Vertx要求使用一种称为event loop线程的处理程序。

如无有 Vert.x 或您的应用程序块中,event loop可以欢快地运行将事件传递给不同的处理程序提供事件陆续到达。

因为没有阻塞,event loop可以在短时间内提供大量的事件。例如一个单一的event loop可以非常迅速地处理成千上万的 HTTP 请求。

我们把这个叫做反应器模式(Reactor Pattern).

你可能会有之前听说过-例如 Node.js 实现此模式。

标准的Reactor所有事件都运行在单一事件循环线程。

单个线程的麻烦是在任何一个时间它只能运行在单一的核心上(例如 Node.js 应用,如果想要实现多线程你要做很多事 。

而Vert.x 不同。不是单事件循环,每个 Vertx 实例都维护若干个事件循环。默认情况下,我们选择数量基于在机器上可用的内核数,但可以自己设置。

与 Node.js 不同是Vertx进程是可配置的,与 Node.js 不同

我们称这种模式多反应器(Multi-Reactor)模式,以区别于单线程的反应器模式。

注意:即使 Vertx 实例维护多个事件循环,任何特定的处理程序将永远不会被同时执行,在大多数情况下 (除了 worker verticles) 将始终使用完全相同的事件循环调用。

黄金法则 — 不要阻塞事件循环

我们已经知道 Vert.x Api 是非阻塞,并且不会堵塞事件循环。
如果你堵塞事件循环,那事件循环将不能做别的事,因为它被阻塞了。如果所有的event loop被阻塞了,应用程序将完全停止!

所以不要这样做!你已经被警告

阻塞的例子包括:

  • Thread.sleep()
  • 等待锁
  • 等待互斥体或监视器 (例如同步段)
  • 做一个长时间的数据库操作和等待返回
  • 做复杂的计算,需要很长的时间。
  • 死循环。

如果有上述情况停止了事件循环(event loop),需要相当长的时间,你应经立即去下一步,并等待进一步的指示。

这个时间具体多长?

具体多长时间?它取决于应用程序需要的并发量。

如果你有一个单一的事件循环,并且你想要处理每秒 10000 的 http 请求,然后很明显,每个请求不能超过 0.1 ms 要处理,所以你不能阻塞比这更多的时间。

这道数学题并不是困难,作为练习留给读者。

如果您的应用程序不响应,可能你阻塞的事件循环的地方。为了帮助您诊断此类问题,如果它检测到一段时间后事件循环还没有恢复,Vert.x会自动记录警告。如果你在日志中看到这样的警告,那么你就应该去检查应用。

Thread vertx-eventloop-thread-3 has been blocked for 20458 ms

Vert.x 还将提供堆栈跟踪来确定阻塞发生的位置。

如果你想关闭这些警告或更改设置,你可以在创建Vertx对象之前,使用VertxOptions配置。

运行阻塞代码

在完美的世界,将没有战争或饥饿,所有 Api 将使用异步写,阳光明媚,绿色的草地有跳来跳去的兔子和手牵手的小羊羔。

但是,现实世界并不是这样。(你看过新闻最近吗?)

事实是,大多数库,特别是在JVM的生态,Y有许多是同步API,许多的方法有可能阻塞。一个很好的例子是JDBC API - 这是本质上的同步,不管如何努力尝试,Vert.x 不能撒上魔法使之同步。

我们不打算在一夜之间把一切改写成异步,所以我们需要给你提供一个方法,一个Vert.x应用中安全地使用“传统”的阻塞API的方法。

如前所述,直接在事件循环里调用阻塞操作,会妨碍它做任何其他有用的工作。所以你怎么能这样呢?

它是通过调用executeBlocking指定要执行的阻塞的代码和在执行阻塞的代码时调用返回异步结果处理程序。

通过调用executeblocking,执行阻塞代码,当阻塞代码执行完成后通过异步回调的方式返回

vertx.executeBlocking(future -> {
  // Call some blocking API that takes a significant amount of time to return
  String result = someAPI.blockingMethod("hello");
  future.complete(result);
}, res -> {
  System.out.println("The result is: " + res.result());
});

默认情况下,如果 executeBlocking 从相同的上下文 (例如同一垂直实例) 调用几次不同的 executeBlocking 则以串行方式执行 (即一个接一个)。

默认情况下,如果executeBlocking在同一环境(例如同一个verticle实例)多次调用,那么不同的executeBlocking将串行执行(即一个接一个)。

如果不关系执行顺序,调用executeBlocking时可以制定ordered参数为false。在这种情况下 executeBlocking 会与worker pool并行执行。

运行阻塞的代码替代方法是使用worker verticle

worker verticle始终在worker池中的线程执行。

查看原文