天天看點

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實體類之間通過

mapTo

mapFrom

互轉。

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

最後,Json對象也可以轉換為字元串,隻需要調用

encode

方法即可。如果檢視源代碼可以發現

JsonObject

toString

方法也調用了

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

,表示監聽所有可用的位址,預設端口号是

,表示随機選取一個可用的端口号。

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 核心包的一些功能,如果想了解更多資訊,請直接檢視官方文檔。