vertx 異步編程指南 step8-使用RxJava進行反應式編程

到目前爲止,我們已經使用基於回調的API探索了Vert.x堆棧的各個領域。它只是起作用,並且這種編程模型在很多語言的開發人員中都是衆所周知的。然而,它可能會變得有點乏味,特別是當你結合幾個事件源或處理複雜的數據流時。

這正是RxJava發揮的作用,Vert.x可以無縫集成它。

注意
在本指南中,使用了RxJava 2.x,但Vert.x也適用於RxJava 1.x. RxJava 2.x在Reactive-Streams規範的基礎上完全重寫。在2.0 wiki頁面的不同之處瞭解更多信息

啓用RxJava API

除了基於回調的API之外,Vert.x模塊還提供了“Rxified” API。要啓用它,首先將vertx-rx-java2模塊添加到Maven POM文件中:

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-rx-java2</artifactId>
</dependency>

Verticle必須進行修改,以便擴展io.vertx.reactivex.core.AbstractVerticle而不是io.vertx.core.AbstractVerticle這有什麼不同?前一類擴展後者並暴露一個io.vertx.reactivex.core.Vertx領域。

io.vertx.reactivex.core.Vertx定義rxSomething(…​)與基於回調的對應方法等效的額外方法。

讓我們來看看MainVerticle如何更好地瞭解它在實踐中的工作原理:

Single<String> dbVerticleDeployment = vertx.rxDeployVerticle(
  "io.vertx.guides.wiki.database.WikiDatabaseVerticle");

rxDeploy方法不會將Handler<AsyncResult<String>>最終參數作爲參數。相反,它返回一個Single<String>

此外,調用該方法時操作不會啓動。它在你訂閱時開始Single操作完成後,它會發出部署id或使用a指示問題的原因Throwable

按順序部署Verticle

要最終確定MainVerticle重構,我們必須確保部署操作按順序觸發併發生:

dbVerticleDeployment
  .flatMap(id -> { (1)

    Single<String> httpVerticleDeployment = vertx.rxDeployVerticle(
      "io.vertx.guides.wiki.http.HttpServerVerticle",
      new DeploymentOptions().setInstances(2));

    return httpVerticleDeployment;
  })
  .subscribe(id -> startFuture.complete(), startFuture::fail); (2)
  1. flatMap運營商應用功能的結果dbVerticleDeployment它在這裏安排的部署HttpServerVerticle

  2. 訂閱時開始操作。在成功或錯誤時,MainVerticle開始未來可能完成或失敗。

部分“Rxifying” HttpServerVerticle

如果按順序跟隨指南,隨時編輯代碼,那麼HttpServerVerticle類仍然使用基於回調的API。在您可以使用RxJava API 自然執行異步操作之前,即 同時需要重構HttpServerVerticle

導入Vert.x類的RxJava版本

import io.vertx.reactivex.core.AbstractVerticle;
import io.vertx.reactivex.core.http.HttpServer;
import io.vertx.reactivex.ext.auth.AuthProvider;
import io.vertx.reactivex.ext.auth.User;
import io.vertx.reactivex.ext.auth.jwt.JWTAuth;
import io.vertx.reactivex.ext.auth.shiro.ShiroAuth;
import io.vertx.reactivex.ext.web.Router;
import io.vertx.reactivex.ext.web.RoutingContext;
import io.vertx.reactivex.ext.web.client.WebClient;
import io.vertx.reactivex.ext.web.client.HttpResponse; (1)
import io.vertx.reactivex.ext.web.codec.BodyCodec;
import io.vertx.reactivex.ext.web.handler.*;
import io.vertx.reactivex.ext.web.sstore.LocalSessionStore;
import io.vertx.reactivex.ext.web.templ.FreeMarkerTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
  1. 我們的backupHandler()方法仍然使用HttpResponse類,所以它必須導入。事實證明,HttpResponseVert.x提供的RxJava版本可以作爲這種特定情況下的替代品。所述“Rxified”在代碼step-8作爲響應類型由lambda表達式推斷引導庫的文件夾不導入此類。

“Rxified” vertx實例上使用委託

要調用一個方法,io.vertx.core.Vertx當你有一個方法io.vertx.reactivex.core.Vertx,調用該getDelegate()方法。start()在創建以下實例時需要調整Verticle的方法 WikiDatabaseService

@Override
public void start(Future<Void> startFuture) throws Exception {

  String wikiDbQueue = config().getString(CONFIG_WIKIDB_QUEUE, "wikidb.queue");
  dbService = io.vertx.guides.wiki.database.WikiDatabaseService.createProxy(vertx.getDelegate(), wikiDbQueue);

同時執行授權查詢

在前面的例子中,我們看到了如何使用RxJava操作符和Rxified Vert.x API按順序執行異步操作。但有時候這種擔保不是必需的,或者您只是希望它們爲了性能原因而同時運行。

JWT令牌生成過程HttpServerVerticle就是這種情況的一個很好的例子。要創建令牌,我們需要完成所有授權查詢,但查詢彼此獨立:

auth.rxAuthenticate(creds).flatMap(user -> {
  Single<Boolean> create = user.rxIsAuthorised("create"); (1)
  Single<Boolean> delete = user.rxIsAuthorised("delete");
  Single<Boolean> update = user.rxIsAuthorised("update");

  return Single.zip(create, delete, update, (canCreate, canDelete, canUpdate) -> { (2)
    return jwtAuth.generateToken(
      new JsonObject()
        .put("username", context.request().getHeader("login"))
        .put("canCreate", canCreate)
        .put("canDelete", canDelete)
        .put("canUpdate", canUpdate),
      new JWTOptions()
        .setSubject("Wiki API")
        .setIssuer("Vert.x"));
  });
}).subscribe(token -> {
  context.response().putHeader("Content-Type", "text/plain").end(token);
}, t -> context.fail(401));
  1. Single創建三個對象,表示不同的授權查詢。

  2. 當三個操作成功完成時,zip運算符回調將與結果一起調用。

查詢數據庫

直接查詢

通常,需要單個數據庫查詢來準備對用戶的響應。對於這樣簡單的情況,JDBCClient提供rxQueryXXXrxUpdateXXX方法:

String query = sqlQueries.get(SqlQuery.GET_PAGE_BY_ID);
JsonArray params = new JsonArray().add(id);
Single<ResultSet> resultSet = dbClient.rxQueryWithParams(query, params);

使用數據庫連接

當直接查詢不適合時(例如,當多個查詢必須參與相同的事務時),您可以從池中獲取數據庫連接。所有你需要做的是呼籲rxGetConnectionJDBCClient

Single<SQLConnection> connection = dbClient.rxGetConnection();

該方法返回一個Single<Connection>您可以輕鬆轉換flatMapXXX以執行SQL查詢的方法:

connection
  .flatMapCompletable(conn -> conn.rxExecute(sqlQueries.get(SqlQuery.CREATE_PAGES_TABLE)))

但是如果SQLConnection參考不再可達,我們怎麼能釋放連接呢?一個簡單而方便的方法是closedoFinally回調中調用:

private Single<SQLConnection> getConnection() {
  return dbClient.rxGetConnection().flatMap(conn -> {
    Single<SQLConnection> connectionSingle = Single.just(conn); (1)
    return connectionSingle.doFinally(conn::close); (2)
  });
}
  1. 在獲得連接後,我們將其包裝成一個 Single

  2. Single被修改,以調用close一個doFinally回調

現在我們將getConnection隨時使用我們需要的數據庫連接。

縮小回調和RxJava之間的差距

有時,您可能必須將RxJava代碼與基於回調的API混合使用。例如,服務代理接口只能用回調來定義,但實現使用Vert.x Rxified API。

在這種情況下,io.vertx.reactivex.SingleHelper.toObserver類可以適應Handler<AsyncResult<T>>RxJava SingleObserver<T>

@Override
public WikiDatabaseService fetchAllPagesData(Handler<AsyncResult<List<JsonObject>>> resultHandler) { (1)
  dbClient.rxQuery(sqlQueries.get(SqlQuery.ALL_PAGES_DATA))
    .map(ResultSet::getRows)
    .subscribe(SingleHelper.toObserver(resultHandler));  (2)
  return this;
}
  1. fetchAllPagesData是一種異步服務代理操作,用Handler<AsyncResult<List<JsonObject>>>回調定義

  2. toObserver方法適用resultHandler於a SingleObserver<List<JsonObject>>,以便在發佈行列表時調用處理程序。

注意
io.vertx.reactivex.CompletableHelperio.vertx.reactivex.MaybeHelper提供適配器CompletableMaybe

數據流

RxJava不僅善於組合不同的事件源,而且對數據流也非常有幫助。與Vert.x或JDK未來不同,a Flowable不僅發佈一個事件流,而且還發布一系列事件。它配備了大量的數據操作操作符。

我們可以使用其中的一些來重構fetchAllPages數據庫垂直方法:

public WikiDatabaseService fetchAllPages(Handler<AsyncResult<JsonArray>> resultHandler) {
  dbClient.rxQuery(sqlQueries.get(SqlQuery.ALL_PAGES))
    .flatMapPublisher(res -> {  (1)
      List<JsonArray> results = res.getResults();
      return Flowable.fromIterable(results); (2)
    })
    .map(json -> json.getString(0)) (3)
    .sorted() (4)
    .collect(JsonArray::new, JsonArray::add) (5)
    .subscribe(SingleHelper.toObserver(resultHandler));
  return this;
}
  1. flatMapPublisher我們將創建一個Flowable從發射的項目Single<Result>

  2. fromIterable將數據庫結果Iterable轉換爲Flowable發出數據庫行項目。

  3. 由於我們只需要頁面名稱,我們可以將map每一JsonObject行記錄到第一列。

  4. 客戶希望數據sorted按字母順序排列。

  5. 事件巴士服務答覆包含在一個單一的JsonArraycollect創建一個新的JsonArray::new項目,隨後添加項目JsonArray::add




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章