Vert.x學(xué)習(xí)筆記(一) Vert.x 核心包

Vert.x是一個事件驅(qū)動的JVM上的框架,可以幫助我們構(gòu)建現(xiàn)代、靈活、可擴展的程序。Vert.x有多種語言的版本,可以用在Java、Kotlin、Scala、Groovy、Ruby等語言上。當(dāng)然現(xiàn)在討論的是如何在Java上使用Vert.x。

Vert.x是一個比較大的框架,包含了各個方面的功能。所以我決定寫幾篇文章,分別來介紹這些功能。所以今天先來看看Vert.x最核心的一些功能吧,這些功能都在vertx-core包下。官方的英文文檔在這里,本文參考和引用了Vertx官方文檔上的一些內(nèi)容,如果需要詳細(xì)信息請直接看官方文檔。當(dāng)然我又發(fā)現(xiàn)了志愿者翻譯的中文文檔,質(zhì)量也可以,只不過版本稍微落后一些。

Vert.x核心庫包含了以下一些功能,它們都是比較底層的功能,開發(fā)者可以根據(jù)需要使用。當(dāng)然由于Vert.x的功能很多,所以這里我不打算全部介紹,只準(zhǔn)備介紹一些比較常用的功能。如果想了解全部功能的話,還是請參考官方文檔。

  • TCP客戶端和服務(wù)端
  • HTTP客戶端和服務(wù)端以及WebSockets支持
  • 事件總線
  • 共享數(shù)據(jù),包括本地maps以及分布式聚簇maps
  • 周期性和延遲操作
  • 數(shù)據(jù)報
  • DNS客戶端
  • 文件系統(tǒng)訪問
  • 高可用性
  • 聚簇

Vert.x的特點是事件驅(qū)動、流式編程和非阻塞,這些特點將會在后面逐一介紹。

引入依賴

如果使用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'
}

開始使用

創(chuàng)建Vertx對象

要使用Vertx的第一步就是創(chuàng)建Vertx對象,所有API都要通過這個對象來調(diào)用。一般情況下,一個程序只需要一個Vertx對象即可,不過有時候為了程序隔離等原因,我們會需要多個Vertx對象。創(chuàng)建Vertx對象很簡單,調(diào)用下面一行代碼即可。

Vertx vertx = Vertx.vertx();

有時候可能需要對Vertx進行一些配置,可以通過添加程序參數(shù)來實現(xiàn)。

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

Verticles

Verticles是Vertx中的一個模型,可以幫助我們封裝代碼。Verticles是一個可選的模型,所以即使我們不使用Verticles,也可以繼續(xù)使用Vertx。Verticles說起來很簡單,就是一個接口。當(dāng)然實際情況下,一般都是繼承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抽象類之后,必須實現(xiàn)的方法是start(),它會在Verticle部署的時候調(diào)用,還有一個可選的方法stop(),在Verticle停止的時候調(diào)用。

public class MyVerticle extends AbstractVerticle {

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

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

}

如果需要異步Verticle,繼承并實現(xiàn)方法簽名帶有Future的那幾個方法即可。

使用JSON

Java中沒有對JSON的原生支持,所以Vertx首先就對這些數(shù)據(jù)類型進行了支持。

JSON對象

首先先來看看JSON對象。我們可以由字符串創(chuàng)建JSON對象。

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

也可以由Map來創(chuàng)建Json對象。

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

當(dāng)然也可以直接創(chuàng)建JsonObj對象。JsonObject的默認(rèn)構(gòu)造函數(shù)會創(chuàng)建一個空J(rèn)son對象,然后我們可以向其中填充數(shù)據(jù)。這個對象支持流式操作,所以可以直接把多個put方法連續(xù)調(diào)用。

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

如果要獲取Json對象的屬性值也很簡單,調(diào)用相應(yīng)的getXXX方法即可。

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

Json對象也可以和Java實體類之間通過mapTomapFrom互轉(zhuǎn)。

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

最后,Json對象也可以轉(zhuǎn)換為字符串,只需要調(diào)用encode方法即可。如果查看源代碼可以發(fā)現(xiàn)JsonObjecttoString方法也調(diào)用了encode方法,所以通過toString方法也可以轉(zhuǎn)為字符串(不過有點多此一舉的意思)。

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

JSON數(shù)組

如果要創(chuàng)建Json數(shù)組,使用JsonArray類。它的使用方法和JsonObject類似。

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

//獲取Json數(shù)組的元素
String name = jsonArray.getString(0);
int age = jsonArray.getInteger(1);
System.out.printf("name:%s,age:%d", name, age);

Buffer

Buffer是Vertx中通用的一種傳遞數(shù)據(jù)的方式,所以先來介紹一下它。

創(chuàng)建Buffer

有以下幾種創(chuàng)建Buffer的方式。如果預(yù)先知道需要數(shù)據(jù)的大小,可以使用最后一種方式,在創(chuàng)建的同時指定Buffer的大小。

//創(chuàng)建空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");
//創(chuàng)建帶初始大小的Buffer
Buffer buffer5 = Buffer.buffer(1024);

寫入Buffer

有兩種寫入Buffer的方式,追加寫入(appendXXX)和隨機寫入(setXXX),這些方法對于各種常用類型做了重載,可以滿足我們各種需求。顧名思義,追加寫入會將數(shù)據(jù)寫入Buffer的最后;隨機寫入可以修改Buffer任何位置的數(shù)據(jù)。Buffer可以自動擴容,所以不必?fù)?dān)心出現(xiàn)IndexOutOfBoundsException。

Buffer buffer = Buffer.buffer();
//追加寫入方式
buffer.appendString("some text");
//隨機寫入方式
buffer.setString(10, "abcde");

讀取Buffer

從Buffer讀取數(shù)據(jù)使用getXXX方法即可。

//讀取數(shù)據(jù)
for (int i = 0; i < buffer.length(); ++i) {
    System.out.print(buffer.getShort(i));
}

當(dāng)把Buffer提交到網(wǎng)絡(luò)套接字等目的地后,Buffer就不能被重用了。

TCP服務(wù)端和客戶端

TCP服務(wù)端

首先需要使用Vertx對象創(chuàng)建一個TCP服務(wù)器。

NetServer server = vertx.createNetServer();

如果需要配置服務(wù)器的屬性,可以在創(chuàng)建的時候傳遞一個NetServerOptions類型參數(shù)。

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

要讓服務(wù)器開始監(jiān)聽,使用下面代碼即可。

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

當(dāng)然也可以在監(jiān)聽的時候指定端口號等屬性,這時候會覆蓋前面設(shè)置的NetServerOptions屬性。默認(rèn)地址是0.0.0.0,表示監(jiān)聽所有可用的地址,默認(rèn)端口號是0,表示隨機選取一個可用的端口號。

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

如果希望及時獲取服務(wù)器監(jiān)聽的結(jié)果,可以使用下面的形式,通過lambda表達式來及時得知監(jiān)聽成功與否。

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

如果使用隨機端口號,那么需要在監(jiān)聽成功之后獲取TCP服務(wù)器使用的端口號。

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

如果要從套接字獲取數(shù)據(jù),需要以下的代碼。

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

如果要向套接字獲取寫入數(shù)據(jù),可以利用前面介紹的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");

最后,要關(guān)閉服務(wù)器,調(diào)用close方法。當(dāng)然也可以檢查關(guān)閉結(jié)果。

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

前面我們使用了connectHandler來讀取套接字傳遞來的數(shù)據(jù),當(dāng)然還有幾個Handler可供使用。closeHandler在服務(wù)器關(guān)閉的時候通知我們,而exceptionHandler會將所有異常報告給我們。

TCP客戶端

要創(chuàng)建TCP客戶端很簡單。

NetClient client = vertx.createNetClient();

類似地,也可以在創(chuàng)建的時候指定配置。

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

創(chuàng)建客戶端之后,需要和服務(wù)器進行連接。

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

HTTP服務(wù)器和客戶端

HTTP服務(wù)器

創(chuàng)建HTTP服務(wù)器很簡單。

HttpServer server = vertx.createHttpServer();

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

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

HttpServer server = vertx.createHttpServer(options);

創(chuàng)建服務(wù)器之后,還需要監(jiān)聽端口。默認(rèn)地址是0.0.0.0,默認(rèn)端口號是80。

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

//監(jiān)聽指定端口
server.listen(8080, "myhost.com");

如果要確定是否監(jiān)聽成功,可以使用下面的代碼。

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

要處理發(fā)送過來的HTTP請求,使用requestHandler。Handler內(nèi)部的request參數(shù)有很多屬性和方法可以幫助我們獲取相應(yīng)的數(shù)據(jù)。熟悉Java Servlet編程的同學(xué)應(yīng)該會感到很親切。這里就不詳細(xì)介紹了。

server.requestHandler(request -> {
  // 在這里編寫代碼
});

要返回響應(yīng),需要Response對象。

HttpServerResponse response = request.response();
response.write(buffer);
//直接返回字符串也可以
response.write("hello world!");
//輸出完響應(yīng)之后需要關(guān)閉相應(yīng)流
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客戶端

要創(chuàng)建HTTP客戶端很簡單。

HttpClient client = vertx.createHttpClient();

如果要增加配置,可以這樣。

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

如果要發(fā)起請求,調(diào)用客戶端的相應(yīng)方法即可。

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容