非同期ドライブ上のI、
バージョン3.0は、MongoDBはアプリケーションのための高性能オプションを提供駆動非同期モード(Javaの非同期ドライバ)を、提供を開始しました。
しかし、実際には、同期駆動(Java同期ドライバ)プロジェクトの使用も少なく、おそらく理由は先入観の(同期ドライバは、より完璧な文書化)、またはそれは、MongoDBの以前のバージョンとの互換性のためです。
いずれにせよ、開発リ未来への非同期ドライブを使用するための傾向でなければなりません。
非同期ドライバを使用する前に、反応性の概念にある程度の知識を持っている必要があります。
第二に、反応性の理解(応答)
(反応性)応答は、非同期データフロー指向の開発モードでは、.NETプラットフォームライブラリーから第一の反応性拡張は、その後、様々なプログラミング言語を達成するように拡張されます。
反応有名な宣言(応答宣言)、反応性の4つの特性の定義:
- タイムリーなレスポンス(応答):システムは時間内に要求に応答することができます。
- そこ靭性(弾力性):例外が発生したときにシステムがまだフォールトトレランスをサポートすることである、応答することができます。
- 伸縮性(弾性):異なる負荷の下で、システムは、伸縮性に実行することを保証することができます。
- メッセージ駆動型(メッセージ駆動型):対話するために異なるコンポーネント間の非同期メッセージングを使用して、緩く結合され、互いに分離することを確実にします。
応答機能が宣言で定義されたこれらのシステムでは、すべての複数のストリームとの間の関係に応答があり、従って、(反応性ストリーム仕様)2013で開始された応答フロー仕様があります。
https://www.reactive-streams.org/
ここで、処理チェーンの流れに応じては、以下の定義を行っています。
- 決して端を流通させている要素の数に制限を処理する能力
- シーケンシャル処理
- 非同期転送要素
- ノンブロッキング負圧(背圧)
Javaプラットフォームは、JDKのバージョン9で反応ストリームのサポートを発表しています。
ここで式の流れに応じていくつかの重要なインターフェイスは、次のとおり
- 出版社の
出版社は、データの出版社です。出版社・インタフェースは、唯一の方法はそれが加入者である、データを追加するために、加入者のために、サブスクライブしています。 - 加入者のための最大
加入者のためのアップ加入者データです。加入者インタフェースは、プロセッサが異なる事象であるように、4つの方法を持っています。出版社に成功したサブスクリプション加入した後、それonSubscribe(サブスクリプションS)メソッドが呼び出されます。
サブスクリプション現在のサブスクリプションの関係を表しています。
サブスクリプションが成功すると、サブスクリプション要求(ロングN)の使用はメソッドn個のデータを公開するパブリッシャーを要求します。パブリッシャは、それぞれ加入者のコールバックメソッドの他の3つに対応する3つの異なる通知メッセージを生成することができます。
データ通知:方法、サイト運営者の表現によって生成されたデータをonNext相当します。
エラー通知:のonErrorメソッドは、出版社がエラーを起こし示すに対応しています。
終了通知:onCompleteの方法は、すべてのデータの解放を完了した出版社を示すに対応しています。
通知、エラー通知と終了通知の上記3種類の、つまり、終了を通知するための通知の終了後、他の通知が発生していないが存在することになるています。
- サブスクリプション
サブスクリプションは、サブスクリプションの関係で表されます。前述の要求方法に加えて、サブスクリプションをキャンセルする方法がキャンセル。cancelメソッドが呼び出された後、発行者はまだ発表を作り続けることが可能である、ということに注意してください。しかし、サブスクリプションは、最終的にキャンセルされます。
これらのインタフェースの関係は下記のように:
写真出典:のhttp://wiki.jikexueyuan.com/index.php/project/reactor-2.0/05.html
MongoDB 的异步驱动为 mongo-java-driver-reactivestreams 组件,其实现了 Reactive Stream 的上述接口。
> 除了 reactivestream 之外,MongoDB 的异步驱动还包含 RxJava 等风格的版本,有兴趣的读者可以进一步了解
http://mongodb.github.io/mongo-java-driver-reactivestreams/1.11/getting-started/quick-tour-primer/
三、使用示例
接下来,通过一个简单的例子来演示一下 Reactive 方式的代码风格:
A. 引入依赖
org.mongodb
mongodb-driver-reactivestreams
1.11.0
> 引入mongodb-driver-reactivestreams 将会自动添加 reactive-streams, bson, mongodb-driver-async组件
B. 连接数据库
//服务器实例表
List servers = new ArrayList();
servers.add(new ServerAddress("localhost", 27018));
//配置构建器
MongoClientSettings.Builder settingsBuilder = MongoClientSettings.builder();
//传入服务器实例
settingsBuilder.applyToClusterSettings(
builder -> builder.hosts(servers));
//构建 Client 实例
MongoClient mongoClient = MongoClients.create(settingsBuilder.build());
C. 实现文档查询
//获得数据库对象
MongoDatabase database = client.getDatabase(databaseName);
//获得集合
MongoCollection collection = database.getCollection(collectionName);
//异步返回Publisher
FindPublisher publisher = collection.find();
//订阅实现
publisher.subscribe(new Subscriber() {
@Override
public void onSubscribe(Subscription s) {
System.out.println("start...");
//执行请求
s.request(Integer.MAX_VALUE);
}
@Override
public void onNext(Document document) {
//获得文档
System.out.println("Document:" + document.toJson());
}
@Override
public void onError(Throwable t) {
System.out.println("error occurs.");
}
@Override
public void onComplete() {
System.out.println("finished.");
}
});
注意到,与使用同步驱动不同的是,collection.find()方法返回的不是 Cursor,而是一个 FindPublisher对象,这是Publisher接口的一层扩展。
而且,在返回 Publisher 对象时,此时并没有产生真正的数据库IO请求。 真正发起请求需要通过调用 Subscription.request()方法。
在上面的代码中,为了读取由 Publisher 产生的结果,通过自定义一个Subscriber,在onSubscribe 事件触发时就执行 数据库的请求,之后分别对 onNext、onError、onComplete进行处理。
尽管这种实现方式是纯异步的,但在使用上比较繁琐。试想如果对于每个数据库操作都要完成一个Subscriber 逻辑,那么开发的工作量是巨大的。
为了尽可能复用重复的逻辑,可以对Subscriber的逻辑做一层封装,包含如下功能:
- 使用 List 容器对请求结果进行缓存
- 結果を待ってブロックされて達成するための方法、あなたはタイムアウトを指定することができます
- 結果を待っている間にスローされた例外をキャッチ
コードは以下の通りであります:
public class ObservableSubscriber implements Subscriber {
//响应数据
private final List received;
//错误信息
private final List errors;
//等待对象
private final CountDownLatch latch;
//订阅器
private volatile Subscription subscription;
//是否完成
private volatile boolean completed;
public ObservableSubscriber() {
this.received = new ArrayList();
this.errors = new ArrayList();
this.latch = new CountDownLatch(1);
}
@Override
public void onSubscribe(final Subscription s) {
subscription = s;
}
@Override
public void onNext(final T t) {
received.add(t);
}
@Override
public void onError(final Throwable t) {
errors.add(t);
onComplete();
}
@Override
public void onComplete() {
completed = true;
latch.countDown();
}
public Subscription getSubscription() {
return subscription;
}
public List getReceived() {
return received;
}
public Throwable getError() {
if (errors.size() > 0) {
return errors.get(0);
}
return null;
}
public boolean isCompleted() {
return completed;
}
/**
* 阻塞一定时间等待结果
*
* @param timeout
* @param unit
* @return
* @throws Throwable
*/
public List get(final long timeout, final TimeUnit unit) throws Throwable {
return await(timeout, unit).getReceived();
}
/**
* 一直阻塞等待请求完成
*
* @return
* @throws Throwable
*/
public ObservableSubscriber await() throws Throwable {
return await(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
}
/**
* 阻塞一定时间等待完成
*
* @param timeout
* @param unit
* @return
* @throws Throwable
*/
public ObservableSubscriber await(final long timeout, final TimeUnit unit) throws Throwable {
subscription.request(Integer.MAX_VALUE);
if (!latch.await(timeout, unit)) {
throw new MongoTimeoutException("Publisher onComplete timed out");
}
if (!errors.isEmpty()) {
throw errors.get(0);
}
return this;
}
}
この基本的なツールで、我々は非同期操作が簡単になるために文書化します。
例えば、以下のように文書を操作するためのクエリを修正することができます。
ObservableSubscriber subscriber = new ObservableSubscriber();
collection.find().subscribe(subscriber);
//结果处理
subscriber.get(15, TimeUnit.SECONDS).forEach( d -> {
System.out.println("Document:" + d.toJson());
});
もちろん、この例はまた、キャッシュとしてリストを使用するなど、改善し続けることができますメモリにワンタイム文書のすべて(または過剰)を避けるために、データの問題の量を考慮する必要があります。