Vert.x学习笔记(一) Vert.x 核心包

Vert.x是一个事件驱动的JVM上的框架,可以帮助我们构建现代、灵活、可扩展的程序。Vert.x有多种语言的版本,可以用在Java、Kotlin、Scala、Groovy、Ruby等语言上。当然现在讨论的是如何在Java上使用Vert.x。

Vert.x是一个比较大的框架,包含了各个方面的功能。所以我决定写几篇文章,分别来介绍这些功能。所以今天先来看看Vert.x最核心的一些功能吧,这些功能都在vertx-core包下。官方的英文文档在这里,本文参考和引用了Vertx官方文档上的一些内容,如果需要详细信息请直接看官方文档。当然我又发现了志愿者翻译的中文文档,质量也可以,只不过版本稍微落后一些。

Vert.x核心库包含了以下一些功能,它们都是比较底层的功能,开发者可以根据需要使用。当然由于Vert.x的功能很多,所以这里我不打算全部介绍,只准备介绍一些比较常用的功能。如果想了解全部功能的话,还是请参考官方文档。

  • TCP客户端和服务端
  • HTTP客户端和服务端以及WebSockets支持
  • 事件总线
  • 共享数据,包括本地maps以及分布式聚簇maps
  • 周期性和延迟操作
  • 数据报
  • DNS客户端
  • 文件系统访问
  • 高可用性
  • 聚簇

Vert.x的特点是事件驱动、流式编程和非阻塞,这些特点将会在后面逐一介绍。

引入依赖

如果使用Maven的话,在pom.xml中添加以下一段即可。

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-core</artifactId>
  <version>3.4.2</version>
</dependency>

如果使用Gradle的话,在build.gradle中添加以下一段。

dependencies {
  compile 'io.vertx:vertx-core:3.4.2'
}

开始使用

创建Vertx对象

要使用Vertx的第一步就是创建Vertx对象,所有API都要通过这个对象来调用。一般情况下,一个程序只需要一个Vertx对象即可,不过有时候为了程序隔离等原因,我们会需要多个Vertx对象。创建Vertx对象很简单,调用下面一行代码即可。

Vertx vertx = Vertx.vertx();

有时候可能需要对Vertx进行一些配置,可以通过添加程序参数来实现。

VertxOptions options = new VertxOptions();
options.setWorkerPoolSize(20);
Vertx vertx2 = Vertx.vertx(options);

Verticles

Verticles是Vertx中的一个模型,可以帮助我们封装代码。Verticles是一个可选的模型,所以即使我们不使用Verticles,也可以继续使用Vertx。Verticles说起来很简单,就是一个接口。当然实际情况下,一般都是继承AbstractVerticle抽象类。

public abstract class AbstractVerticle implements Verticle {

  protected Vertx vertx;

  protected Context context;
  @Override
  public Vertx getVertx() {
    return vertx;
  }
  @Override
  public void init(Vertx vertx, Context context) {
    this.vertx = vertx;
    this.context = context;
  }

  public String deploymentID() {
    return context.deploymentID();
  }
  public JsonObject config() {
    return context.config();
  }
  public List<String> processArgs() {
    return context.processArgs();
  }
  @Override
  public void start(Future<Void> startFuture) throws Exception {
    start();
    startFuture.complete();
  }
  @Override
  public void stop(Future<Void> stopFuture) throws Exception {
    stop();
    stopFuture.complete();
  }
  public void start() throws Exception {
  }
  public void stop() throws Exception {
  }

}

继承AbstractVerticle抽象类之后,必须实现的方法是start(),它会在Verticle部署的时候调用,还有一个可选的方法stop(),在Verticle停止的时候调用。

public class MyVerticle extends AbstractVerticle {

  // Called when verticle is deployed
  public void start() {
  }

  // Optional - called when verticle is undeployed
  public void stop() {
  }

}

如果需要异步Verticle,继承并实现方法签名带有Future的那几个方法即可。

使用JSON

Java中没有对JSON的原生支持,所以Vertx首先就对这些数据类型进行了支持。

JSON对象

首先先来看看JSON对象。我们可以由字符串创建JSON对象。

String jsonString = "{\"name\":\"yitian\",\"age\":25}";
JsonObject jsonObject = new JsonObject(jsonString);
System.out.println(jsonObject);

也可以由Map来创建Json对象。

Map<String, Object> map = new HashMap<>();
map.put("name", "yitian");
map.put("age", 25);
JsonObject jsonObject2 = new JsonObject(map);
System.out.println(jsonObject2);

当然也可以直接创建JsonObj对象。JsonObject的默认构造函数会创建一个空Json对象,然后我们可以向其中填充数据。这个对象支持流式操作,所以可以直接把多个put方法连续调用。

JsonObject jsonObject3 = new JsonObject();
jsonObject3.put("name", "yitian").put("age", 25);
System.out.println(jsonObject3);

如果要获取Json对象的属性值也很简单,调用相应的getXXX方法即可。

String name = jsonObject.getString("name");
int age = jsonObject.getInteger("age");
System.out.printf("name:%s,age:%d", name, age);

Json对象也可以和Java实体类之间通过mapTomapFrom互转。

User user = jsonObject.mapTo(User.class);
System.out.println(user);
JsonObject userObject = JsonObject.mapFrom(user);
System.out.println(userObject);

最后,Json对象也可以转换为字符串,只需要调用encode方法即可。如果查看源代码可以发现JsonObjecttoString方法也调用了encode方法,所以通过toString方法也可以转为字符串(不过有点多此一举的意思)。

String stringValue = jsonObject.encode();
System.out.println(stringValue);

JSON数组

如果要创建Json数组,使用JsonArray类。它的使用方法和JsonObject类似。

JsonArray jsonArray = new JsonArray();
jsonArray.add("yitian").add(25).add(true);
System.out.println(jsonArray);
System.out.println(jsonArray.encode());

//获取Json数组的元素
String name = jsonArray.getString(0);
int age = jsonArray.getInteger(1);
System.out.printf("name:%s,age:%d", name, age);

Buffer

Buffer是Vertx中通用的一种传递数据的方式,所以先来介绍一下它。

创建Buffer

有以下几种创建Buffer的方式。如果预先知道需要数据的大小,可以使用最后一种方式,在创建的同时指定Buffer的大小。

//创建空Buffer
Buffer buffer1 = Buffer.buffer();
Buffer buffer2 = Buffer.buffer(new byte[]{1, 2, 3, 4, 5});
Buffer buffer3 = Buffer.buffer("abcde");
Buffer buffer4 = Buffer.buffer("一二三四五", "utf-8");
//创建带初始大小的Buffer
Buffer buffer5 = Buffer.buffer(1024);

写入Buffer

有两种写入Buffer的方式,追加写入(appendXXX)和随机写入(setXXX),这些方法对于各种常用类型做了重载,可以满足我们各种需求。顾名思义,追加写入会将数据写入Buffer的最后;随机写入可以修改Buffer任何位置的数据。Buffer可以自动扩容,所以不必担心出现IndexOutOfBoundsException

Buffer buffer = Buffer.buffer();
//追加写入方式
buffer.appendString("some text");
//随机写入方式
buffer.setString(10, "abcde");

读取Buffer

从Buffer读取数据使用getXXX方法即可。

//读取数据
for (int i = 0; i < buffer.length(); ++i) {
    System.out.print(buffer.getShort(i));
}

当把Buffer提交到网络套接字等目的地后,Buffer就不能被重用了。

TCP服务端和客户端

TCP服务端

首先需要使用Vertx对象创建一个TCP服务器。

NetServer server = vertx.createNetServer();

如果需要配置服务器的属性,可以在创建的时候传递一个NetServerOptions类型参数。

NetServerOptions options = new NetServerOptions().setPort(4321);
NetServer server = vertx.createNetServer(options);

要让服务器开始监听,使用下面代码即可。

NetServer server = vertx.createNetServer();
server.listen();

当然也可以在监听的时候指定端口号等属性,这时候会覆盖前面设置的NetServerOptions属性。默认地址是0.0.0.0,表示监听所有可用的地址,默认端口号是0,表示随机选取一个可用的端口号。

NetServer server = vertx.createNetServer();
server.listen(1234, "localhost");

如果希望及时获取服务器监听的结果,可以使用下面的形式,通过lambda表达式来及时得知监听成功与否。

NetServer server = vertx.createNetServer();
server.listen(1234, "localhost", res -> {
  if (res.succeeded()) {
    System.out.println("Server is now listening!");
  } else {
    System.out.println("Failed to bind!");
  }
});

如果使用随机端口号,那么需要在监听成功之后获取TCP服务器使用的端口号。

NetServer server = vertx.createNetServer();
server.listen(0, "localhost", res -> {
  if (res.succeeded()) {
    System.out.println("Server is now listening on actual port: " + server.actualPort());
  } else {
    System.out.println("Failed to bind!");
  }
});

如果要从套接字获取数据,需要以下的代码。

NetServer server = vertx.createNetServer();
server.connectHandler(socket -> {
  socket.handler(buffer -> {
    System.out.println("I received some bytes: " + buffer.length());
  });
});

如果要向套接字获取写入数据,可以利用前面介绍的Buffer。需要注意,一旦将Buffer写入套接字,那么Buffer将会失效,无法重用。

Buffer buffer = Buffer.buffer().appendFloat(12.34f).appendInt(123);
socket.write(buffer);

// Write a string in UTF-8 encoding
socket.write("some data");

// Write a string using the specified encoding
socket.write("some data", "UTF-16");

最后,要关闭服务器,调用close方法。当然也可以检查关闭结果。

server.close(res -> {
  if (res.succeeded()) {
    System.out.println("Server is now closed");
  } else {
    System.out.println("close failed");
  }
});

前面我们使用了connectHandler来读取套接字传递来的数据,当然还有几个Handler可供使用。closeHandler在服务器关闭的时候通知我们,而exceptionHandler会将所有异常报告给我们。

TCP客户端

要创建TCP客户端很简单。

NetClient client = vertx.createNetClient();

类似地,也可以在创建的时候指定配置。

NetClientOptions options = new NetClientOptions().setConnectTimeout(10000);
NetClient client = vertx.createNetClient(options);

创建客户端之后,需要和服务器进行连接。

NetClientOptions options = new NetClientOptions().setConnectTimeout(10000);
NetClient client = vertx.createNetClient(options);
client.connect(4321, "localhost", res -> {
  if (res.succeeded()) {
    System.out.println("Connected!");
    //用Socket操作数据
    NetSocket socket = res.result();
  } else {
    System.out.println("Failed to connect: " + res.cause().getMessage());
  }
});

HTTP服务器和客户端

HTTP服务器

创建HTTP服务器很简单。

HttpServer server = vertx.createHttpServer();

如果需要指定配置,也很容易。

HttpServerOptions options = new HttpServerOptions().setMaxWebsocketFrameSize(1000000);

HttpServer server = vertx.createHttpServer(options);

创建服务器之后,还需要监听端口。默认地址是0.0.0.0,默认端口号是80

HttpServer server = vertx.createHttpServer();
server.listen();

//监听指定端口
server.listen(8080, "myhost.com");

如果要确定是否监听成功,可以使用下面的代码。

HttpServer server = vertx.createHttpServer();
server.listen(8080, "myhost.com", res -> {
  if (res.succeeded()) {
    System.out.println("Server is now listening!");
  } else {
    System.out.println("Failed to bind!");
  }
});

要处理发送过来的HTTP请求,使用requestHandler。Handler内部的request参数有很多属性和方法可以帮助我们获取相应的数据。熟悉Java Servlet编程的同学应该会感到很亲切。这里就不详细介绍了。

server.requestHandler(request -> {
  // 在这里编写代码
});

要返回响应,需要Response对象。

HttpServerResponse response = request.response();
response.write(buffer);
//直接返回字符串也可以
response.write("hello world!");
//输出完响应之后需要关闭相应流
response.end();

如果要指定返回的header、content-type等信息,可以用下面的代码。

HttpServerResponse response = request.response();
MultiMap headers = response.headers();
headers.set("content-type", "text/html");
headers.set("other-header", "wibble");

或者直接使用putHeaders方法。

response.putHeader("content-type", "text/html").putHeader("other-header", "wibble");

Vertx还有一些特性,可以帮助我们处理文件上传等情况,不过篇幅所限就不介绍了。

HTTP客户端

要创建HTTP客户端很简单。

HttpClient client = vertx.createHttpClient();

如果要增加配置,可以这样。

HttpClientOptions options = new HttpClientOptions().setKeepAlive(false);
HttpClient client = vertx.createHttpClient(options);

如果要发起请求,调用客户端的相应方法即可。

Vertx vertx = Vertx.vertx();
HttpClient client = vertx.createHttpClient();
client.getNow("httpbin.org", "/get", response -> {
    response.bodyHandler(System.out::println);
});

由于篇幅所限,这里只介绍Vert.x 核心包的一些功能,如果想了解更多信息,请直接查看官方文档。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容