到目前爲止,我們已經使用基於回調的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)
該
flatMap
運營商應用功能的結果dbVerticleDeployment
。它在這裏安排的部署HttpServerVerticle
。訂閱時開始操作。在成功或錯誤時,
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;
我們的
backupHandler()
方法仍然使用HttpResponse
類,所以它必須導入。事實證明,HttpResponse
Vert.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));
Single
創建三個對象,表示不同的授權查詢。當三個操作成功完成時,
zip
運算符回調將與結果一起調用。
查詢數據庫
直接查詢
通常,需要單個數據庫查詢來準備對用戶的響應。對於這樣簡單的情況,JDBCClient
提供rxQueryXXX
和rxUpdateXXX
方法:
String query = sqlQueries.get(SqlQuery.GET_PAGE_BY_ID);
JsonArray params = new JsonArray().add(id);
Single<ResultSet> resultSet = dbClient.rxQueryWithParams(query, params);
使用數據庫連接
當直接查詢不適合時(例如,當多個查詢必須參與相同的事務時),您可以從池中獲取數據庫連接。所有你需要做的是呼籲rxGetConnection
的JDBCClient
:
Single<SQLConnection> connection = dbClient.rxGetConnection();
該方法返回一個Single<Connection>
您可以輕鬆轉換flatMapXXX
以執行SQL查詢的方法:
connection
.flatMapCompletable(conn -> conn.rxExecute(sqlQueries.get(SqlQuery.CREATE_PAGES_TABLE)))
但是如果SQLConnection
參考不再可達,我們怎麼能釋放連接呢?一個簡單而方便的方法是close
在doFinally
回調中調用:
private Single<SQLConnection> getConnection() {
return dbClient.rxGetConnection().flatMap(conn -> {
Single<SQLConnection> connectionSingle = Single.just(conn); (1)
return connectionSingle.doFinally(conn::close); (2)
});
}
在獲得連接後,我們將其包裝成一個
Single
將
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;
}
fetchAllPagesData
是一種異步服務代理操作,用Handler<AsyncResult<List<JsonObject>>>
回調定義。該
toObserver
方法適用resultHandler
於aSingleObserver<List<JsonObject>>
,以便在發佈行列表時調用處理程序。
注意 | io.vertx.reactivex.CompletableHelper 並io.vertx.reactivex.MaybeHelper 提供適配器Completable 和Maybe 。 |
數據流
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;
}
與
flatMapPublisher
我們將創建一個Flowable
從發射的項目Single<Result>
。fromIterable
將數據庫結果Iterable
轉換爲Flowable
發出數據庫行項目。由於我們只需要頁面名稱,我們可以將
map
每一JsonObject
行記錄到第一列。客戶希望數據
sorted
按字母順序排列。事件巴士服務答覆包含在一個單一的
JsonArray
。collect
創建一個新的JsonArray::new
項目,隨後添加項目JsonArray::add
。