版权声明:本文为博主自主翻译,转载请标明出处。 https://blog.csdn.net/elinespace/article/details/80675371
相应代码位于本指南仓库的step-8目录下
截止目前,我们已经探索了Vert.x技术栈的多个部分,使用基于回调的API。它仅仅可以正常工作,而且这个编程模型对于开发者在许多语言中是非常熟悉的。尽管如此,它可能有点繁琐,尤其当你组合几个事件源或者处理复杂的数据流时。
而这正是RxJava闪耀的地方,Vert.x无缝的集成了它。
除了基于回调的API,Vert.x模块提供了一套“Rxified”API。为了启用它,首先需要添加vertx-rx-java模块到Maven POM文件:
io.vertx
vertx-rx-java
Verticle现在需要修改为继承自io.vertx.rxjava.core.AbstractVerticle而不是io.vertx.core.AbstractVerticle。这有什么不同?前一个类扩展了后者,并暴露了一个io.vertx.rxjava.core.Vertx类型的属性。
io.vertx.rxjava.core.Vertx定义了额外的rxSomething(…)方法,这相当于基于回调的对等体。
让我们看一下MainVerticle,以便在实践中更好地了解它是如何工作的:
Single dbVerticleDeployment = vertx.rxDeployVerticle("io.vertx.guides.wiki.database.WikiDatabaseVerticle");
rxDeploy方法没有使用一个Handler
为了完成MainVerticle重构,我们必须确保部署操作被触发并按顺序发生:
dbVerticleDeployment.flatMap(id -> { ①
Single httpVerticleDeployment = vertx.rxDeployVerticle( "io.vertx.guides.wiki.http.HttpServerVerticle",new DeploymentOptions().setInstances(2));
return httpVerticleDeployment;
}).subscribe(id->startFuture.complete(),startFuture::fail); ②
① flatMap方法应用该函数到dbVerticleDeployment的结果。此处它调度HttpServerVerticle的部署。
② 操作是在订阅时启动。根据结果成功还是失败,MainVerticle调用startFuture的complete或者fail方法。
如果你按顺序阅读指南,并按照讲解编辑你的代码,那么你的HttpServerVerticle类依旧使用基于回调的API。在你顺理成章的使用RxJava API执行异步操作之前,如并发,你需要重构HttpServerVerticle。
import io.vertx.rxjava.core.AbstractVerticle;
import io.vertx.rxjava.core.http.HttpServer;
import io.vertx.rxjava.ext.auth.AuthProvider;
import io.vertx.rxjava.ext.auth.User;
import io.vertx.rxjava.ext.auth.jwt.JWTAuth;
import io.vertx.rxjava.ext.auth.shiro.ShiroAuth;
import io.vertx.rxjava.ext.web.Router;
import io.vertx.rxjava.ext.web.RoutingContext;
import io.vertx.rxjava.ext.web.client.WebClient;
import io.vertx.rxjava.ext.web.client.HttpResponse; ①
import io.vertx.rxjava.ext.web.codec.BodyCodec;
import io.vertx.rxjava.ext.web.handler.*;
import io.vertx.rxjava.ext.web.sstore.LocalSessionStore;
import io.vertx.rxjava.ext.web.templ.FreeMarkerTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;
import rx.Single;
① 我们的backupHandler()方法依旧使用HttpResponse类,因此它必须被导入。事实证明,Vert.x提供的RxJava版本的HttpResponse可以作为这种情况下的替代。在本指南仓库step-8目录中的“Rxified”代码没有导入这个类,因为响应类型是由lambda表达式推断的。
当你有一个io.vertx.rxjava.core.Vertx对象,并希望对io.vertx.core.Vertx实例进行方法调用时,可以调用getDelegate()方法。Verticle的start()方法需要调整,当创建一个WikiDatabaseService时:
@Override
public void start(Future 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按顺序执行异步操作。但是有时候这种保证(译者注:指按顺序执行异步操作)是不需要的,或许你只是出于性能原因需要它们简单的并发运行。
HttpServerVerticle的JWT令牌生成过程是这种情况的一个好例子。为了创建一个令牌,我们需要所有的授权查询结果完成,但是查询是相互独立的:
auth.rxAuthenticate(creds).flatMap(user -> {
Single create = user.rxIsAuthorised("create"); ①
Single delete = user.rxIsAuthorised("delete");
Single update = user.rxIsAuthorised("update");
return Single.zip(create, delete, update, (canCreate, canDelete, canUpdate) -> { ②
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上的rxGetConnection方法:
Single connection = dbClient.rxGetConnection();
这个方法返回了一个Single,你可以轻易使用flatMap变换来执行SQL查询:
Single resultSet = dbClient.rxQueryWithParams( sqlQueries.get(SqlQuery.GET_PAGE_BY_ID), new JsonArray().add(id));
但是,如果SQLConnection引用不再可达,我们怎么释放该链接?一个简单而且方便的方法是当Single取消订阅时执行close:
private Single getConnection() {
return dbClient.rxGetConnection().flatMap(conn -> {
Single connectionSingle = Single.just(conn); ①
return connectionSingle.doOnUnsubscribe(conn::close); ②
});
}
① 在获取链接之后,我们将其封装为一个Single。
② Single修改为当取消订阅时,调用close。
现在我们可以在数据库Verticle中任何需要执行SQL查询的时候使用getConnection。
有时,你可能必须混合RxJava代码和基于回调的API。例如,服务代理接口只能定义为回调的方式,但是它的实现使用了Vert.x Rxified API。
这种情况下,io.vertx.rx.java.RxHelper类可以适配Handler
@Override
public WikiDatabaseService fetchAllPagesData(Handler>> resultHandler) { ①
dbClient.rxQuery(sqlQueries.get(SqlQuery.ALL_PAGES_DATA))
.map(ResultSet::getRows)
.subscribe(RxHelper.toSubscriber(resultHandler)); ②
return this;
}
① fetchAllPagesData是一个异步服务代理操作,其定义使用了Handler
RxJava不仅是合并不同事件来源的伟大工具,它对于数据流也非常有帮助。不像Vert.x future或者JDK future,Observable发出一个事件流,而不仅是一个单独的事件,并且它拥有一个广泛的数据操作运算集。
我们可以使用这些操作中一些来重构数据库Verticle中的fetchAllPages方法:
public WikiDatabaseService fetchAllPages(Handler> resultHandler) {
dbClient.rxQuery(sqlQueries.get(SqlQuery.ALL_PAGES))
.flatMapObservable(res -> { ①
List results = res.getResults();
return Observable.from(results); ②
})
.map(json->json.getString(0)) ③
.sorted() ④
.collect(JsonArray::new, JsonArray::add) ⑤
.subscribe(RxHelper.toSubscriber(resultHandler));
return this;
}
① 通过flatMapObservable,我们可以使用Single发出的条目创建一个Observable。
② from将数据库results迭代转换成一个Observable,该Observable发出数据库行条目。
③ 由于我们只需要页面名称,我们可以map每个JsonObject行到首列。
④ 客户端希望数据按照字母表顺序sorted。
⑤ 事件总线服务应答包含在一个单独的JsonArray中。collect方法通过JsonArray::new创建一个新的对象,然后当条目发出时通过JsonArray::add方法添加它们。