Vert.x Core 中文使用手册(持续更新)


声明:本文转载自https://my.oschina.net/u/2342449/blog/1552618,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

1. 开始使用

    使用该框架第一步是创建Vertx对象,该框架的功能都依赖于Vertx,例如创建client,servers,获取event bus,设置timers等等操作。

  • 简单创建Vertx

    可以简单创建Vertx,代码如下:

Vertx vertx =Vertx.vertx()
  • 创建时做某些配置

    如果想配置一些信息,可以这么创建,代码如下:

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

    更多配置信息请阅读VertxOptions文档。

  • 创建集群的Vertx对象

后续补充。

注意:多数情况下只需要创建一个Vertx对象就可以了,不过也有些情况需要创建多个Vertx对象,例如两个总线需要隔离或者客户端服务器需要分组时。(具体解释后续补充)

2. 链式操作

    Vertx的方法会返回自身对象的引用,所以你可以链式调用函数

    链式调用代码:

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

    如果不喜欢链式调用,也可以拆开,代码如下:

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

    上面两段代码没有区别。

3. 回调

    Vertx APIs是事件驱动的,也就是当你感兴趣的事情发生后,Vertx会以发送events的方式通知你

    下面是部分events:

  • 定时器被触发
  • socket接收到了一些数据
  • 数据已经从硬盘中读取
  • 产生了exception
  • HTTP server 接到了请求

    你可以通过向Vertx APIs注册处理函数的方式处理这些events,例如启动一个每秒触发一次的定时器,并注册一个处理这个事件的函数,代码如下:

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

    注意:当事件发生时,Vertx会异步调用处理函数。由于是异步的,下面将介绍一些在Vertx中重要的概念。

4.拒绝阻塞

    绝大部分Vertx APIs是不会阻塞当前线程的。(有非常少的会,例如后续补充)。

    如果代码能够立即得到结果,可以在当前线程执行。如果不能,你通常应该注册一个处理函数,等代码执行完后由Vertx调用处理函数做后续处理。

    通常需要较长时间的操作会发生在以下场景:

  • 从socket中读取数据
  • 向硬盘写数据
  • 发送数据并等待回复
  • 许多其他的情况

    如果在在当前线程执行以上操作,并等待操作执行完(期间该不能做其他事)再做后续的处理,这会很低效(这种方式我称为阻塞式)。

  •    阻塞式的例子
/* 比如说,你以上面所说的方式实现了一个服务器。 现在每秒共有100个请求同时到达,并且其中50个请求需要读取硬盘数据(用时2秒),其余50个能立即完成。 那么10秒内共有1000个请求到达, 第一秒需要开启51个线程,其中50个在等待数据读取操作的完成,其中1个线程处理其余操作 第二秒开始,有1个线程的工作已经完成,50个线程在忙,所以需要再开50个线程处理需要读取硬盘数据的操作 第三秒开始~~~~~~~~~~ 以此类推,阻塞式的处理方式需要开启大量线程才能使你的服务器不卡死 */
  • 非阻塞式
/* 和上面例子情况相关,不过当进行数据读取操作时,线程不会一直等待,而是继续处理其他请求 第一秒需要开启1个线程,其中50请求会开启数据读取操作,当开启后不阻塞,会继续处理其他请求 第二秒开始,有1个线程的工作已经完成,接着向第一秒一样处理请求 第三秒~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~ 以此类推,非阻塞式的处理方式只需要少数线程就能让服务器不卡死 */

    补充说明:在计算机中,有专门的硬件处理硬盘读写操作,当操作完成后会发出中断通知对应程序,也就是硬盘读写不占用cpu。不只是硬盘读写,很多操作也是有专门的硬件处理的,不占用cpu。具体内容请看计算机组成原理(大概是这样,具体是什么我忘了,欢迎知道的补充完整,上面有错误的也欢迎指出)

    线程占用的内存以及不同线程间的切换会带来开销。现在的应用的并发量会很大,阻塞式的处理方式难以满足处理大量并发请求的需求。

5.反应器和多个反应器

    上面提到过,Vertx是事件驱动的,也就是当某个事件发生时,会调用相应的处理函数

    大多数情况下,Vertx使用一个称为event loop的线程调用你的处理函数

    这个event loop会在events到达时将其(指events)分派给不同的处理函数,这样你的程序就不会阻塞。

    因为不会阻塞,一个event loop可以在短时间内分发大量的events。例如单个event loop可以非常快的处理数以千计的HTTP 请求。

    我们称这种模式为 Reactor Pattern(反应器模式)

    补充:上面那个链接是维基百科的,应该需要翻墙才能看,这里给个国内博客的链接:Reactor(反应器)模式初探

    标准的反应器模式实现是单线程的,也就是一个event loop循环监听events的到来,并将其分发到对应的处理函数。(换句话说,一个event loop处理全部的events)

    这种实现方式的缺陷是,任何时候它只能运行在cpu的单核上,如果你想使用cpu的其他核,你必须启动和管理多个进程。下面给出原文,因为怕理解不对

The trouble with a single thread is it can only run on a single core at any one time, so if you want your single threaded reactor application (e.g. your Node.js application) to scale over your multi-core server you have to start up and manage many different processes.

    Vertx的实现方式和上面的不同。每一个Vertx实例持有多个event loop而不是单个event loop。默认情况下会根据机器上可用核的数量决定event loop的数量,当然这个可以被重写。(意思是默认情况下Vertx框架会自动根据机器上cpu的核数决定event loop的数量还是文档建议我们根据核数配置event loop的数量?英语渣的痛苦,下面给出原文)

Vert.x works differently here. Instead of a single event loop, each Vertx instance maintains several event loops. By default we choose the number based on the number of available cores on the machine, but this can be overridden.

     为了和单线程的反应器模式区别,我们称这个模式为多反应器模式(Multi-Reactor Pattern)。

    注意:虽然一个Vertx实例会持有多个event loop,但是任何一个处理函数不会被并发调用。在大多数情况(除了后续补充)下,处理函数会由固定的event loop调用。(换句话说,现在有处理函数A,B,C以及event loop a,b。通常不会发生a和b同时调用A的情况,一般是A固定由b调用,B固定由a调用)

6. 黄金法则 -不要阻塞Event Loop

    上面提到过,Vertx APIs是非阻塞的并且不会阻塞event loop。但是如果你在处理函数内写了阻塞代码,event loop还是会被阻塞。

    如果全部event loop被阻塞了,你的程序就会完全停止。所以别在处理函数内写阻塞代码!

    下面给出一些阻塞代码例子:

  • Thread.sleep()
  • 等待锁
  • 做长时间的数据库操作或者等待结果
  • 做复杂运算
  • 大量循环(不知到理解对没,原文是Spinning in a loop)

    多长时间的操作算阻塞?这个根据实际情况定。例如你希望你的程序每秒能处理10000个HTTP请求,那么单个请求处理时间不能超过0.1ms。也就是说执行时间超过0.1ms就是阻塞了。

    如果检测到某个event loop有一定时间没有返回了,Vertx能自动以日志的形式发出提醒。如果你看到类似下面的日志,你就要注意是否在event loop的哪个地方发生阻塞。

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

    Vertx也提供栈式追踪,从而精确定位发生阻塞的位置。

    如果你向关闭提醒或者更改设置,可以在VertxOptions对象上做相应设置。注意:应该在创建Vertix对象前设置。

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

7.运行阻塞式代码

    理想情况下全部APIs是异步的,然后实际上并不是。

    在JVM技术圈,有许多异步的APIs也有许多阻塞式的APIs。一个很好的例子就是JDBC,它是天然同步的,即使Vertx再牛逼,也不能把它变成异步的。

    Vertx不打算将全部都改写成异步的,而是提供一种安全运行阻塞代码的方式。

    前面我们说过,不要在处理函数内写阻塞式代码,那如何执行阻塞代码呢?

    答案是通过调用excuteBlocking来执行指定的阻塞式代码,并且当阻塞式代码执行完后,处理函数会被异步调用。例子如下:

/* 第一个参数式阻塞式代码,第二个参数式处理函数 */ 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在同一个上下文(context)中多少被调用(例如在同一个Vertix实例),不同的executeBlocking会被顺序执行(一个接着一个)。

    如果你不在意执行顺序,你可以将参数ordered设为false。这样,任何executeBlocking都可能会在工作池中并行执行(一些名称解释后续补充

<T> void executeBlocking(Handler<Future<T>> blockingCodeHandler,                          boolean ordered,                          Handler<AsyncResult<T>> resultHandler)

    另一种运行阻塞式代码的可选方式是使用worker verticleworker verticle总是由来自工作池的线程执行。(具体情况后续补充

    默认情况下,阻塞代码式会在Vertx的工作池中执行,这个工作池由VertxOptions对象的setWorkerPoolSize配置。

    你也可以创建新的工作池用来执行阻塞式代码(除了执行阻塞式代码,应该还有其余用途,后续补充)。示例代码如下:

/* 创建一个WorkerExecuter,对象持有新的线程池来执行代码? 具体情况后续补充 */ WorkerExecutor executor = vertx.createSharedWorkerExecutor("my-worker-pool"); executor.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()); });

    注意:新创建的工作池不再使用的时候必须关闭

executor.close();

    如果多个同名的WorkerExcutor被创建,那么这些WorkerExcutor使用相同的线程池。当全部使用这个线程池的WorkerExcutor被关闭,该线程池会被销毁。

    如果WorkerExcutor是在Verticle中创建的,那么当这个Verticle被unDeployed后,Vertx会自动帮你关闭对应的WorkerExcutor。

    WorkerExcutors是可配置的,实例代码如下:

int poolSize = 10;  // 2 minutes long maxExecuteTime = 120000;  WorkerExecutor executor = vertx.createSharedWorkerExecutor("my-worker-pool", poolSize, maxExecuteTime);

 

本文发表于2017年10月18日 18:34
(c)注:本文转载自https://my.oschina.net/u/2342449/blog/1552618,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 6112 讨论 0 喜欢 2

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1