簡単に言うと、新しいデータだけがネットワークに送信されます。仕組みは次のとおりです。
サブスクリプションを管理するMeteorサーバーには、次の3つの重要な部分があります。サブスクリプションが提供するデータのロジックを定義する公開機能。データベースの変更を監視するMongoドライバー。マージボックスは、クライアントのアクティブなサブスクリプションをすべて組み合わせて、ネットワーク経由でクライアントに送信します。
公開機能
Meteorクライアントがコレクションをサブスクライブするたびに、サーバーは
公開機能を実行します。公開機能の仕事は、クライアントが持つ必要のある一連のドキュメントを把握し、各ドキュメントプロパティをマージボックスに送信することです。これは、新しいサブスクライブクライアントごとに1回実行されます。を使用した任意の複雑なアクセス制御など、任意のJavaScriptを公開関数に配置できますthis.userId
。this.added
公開関数は、、this.changed
および
を呼び出してデータをマージボックスに送信しますthis.removed
。詳細については、
完全な公開ドキュメントを参照してください。
ただし、ほとんどの公開関数は、低レベルadded
のAPIをいじくり回す必要はありません
。公開関数がMongoカーソルを返す場合、MeteorサーバーはMongoドライバーの出力(、、およびコールバック)をマージボックスの入力( 、、および)に自動的に接続します。公開関数ですべての権限チェックを事前に実行してから、ユーザーコードを邪魔することなく、データベースドライバーをマージボックスに直接接続できるのは非常に便利です。また、自動公開をオンにすると、この少しでも非表示になります。サーバーは、各コレクション内のすべてのドキュメントのクエリを自動的に設定し、それらをマージボックスにプッシュします。changed
removed
insert
update
removed
this.added
this.changed
this.removed
一方、データベースクエリの公開に限定されません。たとえば、内部のデバイスからGPS位置を読み取るMeteor.setInterval
、または別のWebサービスからレガシーRESTAPIをポーリングする公開関数を作成できます。added
このような場合、低レベルのDDP APIchanged
を呼び出すことで、マージボックスに変更を加えます。removed
Mongoドライバー
Mongoドライバーの仕事は、ライブクエリへの変更についてMongoデータベースを監視することです。これらのクエリは継続的に実行され、、、、およびコールバックを呼び出すことによって結果が変化すると更新をadded
返しremoved
ますchanged
。
Mongoはリアルタイムデータベースではありません。したがって、ドライバーはポーリングします。アクティブなライブクエリごとに、最後のクエリ結果のメモリ内コピーを保持します。各ポーリングサイクルで、新しい結果を以前に保存された結果と比較し、違いを説明する、、、およびイベントの最小セットを計算added
しremoved
ますchanged
。複数の呼び出し元が同じライブクエリのコールバックを登録する場合、ドライバーはクエリの1つのコピーのみを監視し、登録された各コールバックを同じ結果で呼び出します。
サーバーがコレクションを更新するたびに、ドライバーはそのコレクションの各ライブクエリを再計算します(Meteorの将来のバージョンでは、更新時に再計算するライブクエリを制限するためのスケーリングAPIが公開されます)。ドライバーは、10秒のタイマーで各ライブクエリをポーリングします。 Meteorサーバーをバイパスした帯域外データベースの更新をキャッチします。
マージボックス
マージボックスの役割は、クライアントのすべてのアクティブな公開関数の結果(added
、changed
およびremoved
呼び出し)を単一のデータストリームに結合することです。接続されているクライアントごとに1つのマージボックスがあります。クライアントのminimongoキャッシュの完全なコピーを保持します。
サブスクリプションが1つしかない例では、マージボックスは基本的にパススルーです。ただし、より複雑なアプリには、重複する可能性のある複数のサブスクリプションが含まれる場合があります。2つのサブスクリプションが両方とも同じドキュメントに同じ属性を設定する場合、マージボックスはどちらの値が優先されるかを決定し、それをクライアントにのみ送信します。サブスクリプションの優先度を設定するためのAPIはまだ公開されていません。今のところ、優先順位は、クライアントがデータセットをサブスクライブする順序によって決定されます。クライアントが作成する最初のサブスクリプションが最も優先度が高く、2番目のサブスクリプションが次に高くなります。
マージボックスはクライアントの状態を保持するため、公開関数がクライアントにフィードする内容に関係なく、各クライアントを最新の状態に保つために最小限のデータを送信できます。
アップデートで何が起こるか
これで、シナリオの準備が整いました。
1,000の接続されたクライアントがあります。それぞれが同じライブMongoクエリ(Somestuff.find({})
)にサブスクライブされます。クエリは各クライアントで同じであるため、ドライバは1つのライブクエリのみを実行しています。1,000個のアクティブなマージボックスがあります。また、各クライアントの公開関数は、、、を登録し、
added
そのライブクエリにマージボックスの1つにフィードします。マージボックスには他に何も接続されていません。changed
removed
まず、Mongoドライバー。クライアントの1つがに新しいドキュメントを挿入するSomestuff
と、再計算がトリガーされます。Mongoドライバーは、内のすべてのドキュメントに対してクエリを再実行しSomestuff
、その結果をメモリ内の以前の結果と比較し、新しいドキュメントが1つあることを検出し、1,000個の登録済みinsert
コールバックのそれぞれを呼び出します。
次に、公開機能。ここではほとんど何も起こりません。1,000個のinsert
コールバックのそれぞれが、を呼び出すことによってデータをマージボックスにプッシュしますadded
。
最後に、各マージボックスは、クライアントのキャッシュのメモリ内コピーに対してこれらの新しい属性をチェックします。いずれの場合も、値がまだクライアント上になく、既存の値をシャドウイングしていないことがわかります。したがって、マージボックスは、DATA
クライアントへのSockJS接続でDDPメッセージを発行し、サーバー側のメモリ内コピーを更新します。
合計CPUコストは、1つのMongoクエリを比較するためのコストに加えて、クライアントの状態をチェックして新しいDDPメッセージペイロードを構築する1,000個のマージボックスのコストです。ネットワーク上を流れる唯一のデータは、データベース内の新しいドキュメントに対応する1,000のクライアントのそれぞれに送信される単一のJSONオブジェクトと、元の挿入を行ったクライアントからサーバーへの1つのRPCメッセージです。
最適化
これが私たちが間違いなく計画したことです。
より効率的なMongoドライバー。
0.5.1でドライバーを
最適化して、個別のクエリごとに1つのオブザーバーのみを実行しました。
すべてのDB変更がクエリの再計算をトリガーする必要はありません。自動化された改善を行うこともできますが、最善のアプローチは、開発者が再実行する必要のあるクエリを指定できるAPIです。たとえば、1つのチャットルームにメッセージを挿入しても、2番目のルームのメッセージのライブクエリが無効にならないことは、開発者にとって明らかです。
Mongoドライバー、公開関数、およびマージボックスは、同じプロセスで実行する必要はなく、同じマシン上で実行する必要もありません。一部のアプリケーションは複雑なライブクエリを実行し、データベースを監視するためにより多くのCPUを必要とします。他の人はいくつかの明確なクエリしか持っていませんが(ブログエンジンを想像してください)、おそらく多くの接続されたクライアント-これらはマージボックスのためにより多くのCPUを必要とします。これらのコンポーネントを分離すると、各ピースを個別にスケーリングできます。
多くのデータベースは、行が更新されたときに起動するトリガーをサポートし、古い行と新しい行を提供します。この機能を使用すると、データベースドライバーは、変更をポーリングする代わりにトリガーを登録できます。