多くのクライアント間で膨大なコレクションを共有しながら、Meteorはどれほど効率的ですか?


100

次のケースを想像してみてください:

  • 1,000台のクライアントが「Somestuff」コレクションのコンテンツを表示するMeteorページに接続されています。

  • 「Somestuff」は1,000点のコレクションです。

  • 誰かが「Somestuff」コレクションに新しいアイテムを挿入します

何が起こるか:

  • Meteor.Collectionクライアント上のすべてのが更新されます。つまり、挿入はすべてのクライアントに転送されます(つまり、1つの挿入メッセージが1,000クライアントに送信されます)

サーバーがどのクライアントを更新する必要があるかを判断するためのCPUの面でのコストはどれくらいですか

リスト全体ではなく、挿入された値のみがクライアントに転送されることは正確ですか?

これは実際の生活の中でどのように機能しますか?利用可能なそのような規模のベンチマークまたは実験はありますか?

回答:


119

簡単に言えば、新しいデータだけがネットワーク経由で送信されるということです。これがどのように機能するかです。

Meteorサーバーには、サブスクリプションを管理する3つの重要な部分があります。サブスクリプションが提供するデータのロジックを定義するpublish関数Mongoのドライバの変更のためにデータベースを監視; そして、マージボックスクライアントのアクティブなサブスクリプションのすべてを組み合わせて、クライアントにネットワークを介してそれらを送信し、。

公開機能

Meteorクライアントがコレクションをサブスクライブするたびに、サーバーはパブリッシュ関数を実行 します。公開機能の仕事は、クライアントが持つべきドキュメントのセットを見つけ出し、各ドキュメントプロパティをマージボックスに送信することです。新しいサブスクライブクライアントごとに1回実行されます。を使用した任意の複雑なアクセス制御など、必要なJavaScriptをpublish関数に配置できますthis.userId。publish関数はthis.addedthis.changedおよびを 呼び出して、データをマージボックスに送信しますthis.removed。詳細については、 完全な公開ドキュメントを参照してください。

パブリッシュほとんどの関数は、低レベルの周りマックする必要はありません addedchangedremovedただし API。パブリッシュ機能はMongoのカーソルを返す場合、流星サーバは自動的にMongoのドライバ(の出力接続insertupdateおよびremovedマージボックスの入力にコールバック)( this.addedthis.changedおよびthis.removed)。パブリッシュ関数ですべての権限チェックを事前に実行し、ユーザーコードなしでデータベースドライバーをマージボックスに直接接続できることは非常に便利です。そして、自動公開がオンになっていると、この少しでも非表示になります。サーバーは、各コレクションのすべてのドキュメントに対するクエリを自動的に設定し、それらをマージボックスにプッシュします。

一方、データベースクエリの公開に限定されません。たとえば、内のデバイスからGPS位置を読み取るMeteor.setInterval、または別のWebサービスからレガシーREST APIをポーリングする発行関数を作成できます。これらのケースでは、低レベルを呼び出すことで、マージボックスへの変更を放出するだろうaddedchangedremovedDDP API。

モンゴドライバー

Mongoのドライバーの仕事は、ライブのクエリへの変更のためにモンゴデータベースを監視することです。これらのクエリは継続的に実行され、呼び出しによって結果が変更されると更新を返しますaddedremovedおよびchangedコールバックを、。

Mongoはリアルタイムデータベースではありません。したがって、ドライバーは投票します。アクティブな各ライブクエリの最後のクエリ結果のメモリ内コピーを保持します。各ポーリングサイクルで、新しい結果を以前に保存した結果と比較し、addedremovedおよびchanged 差を記述するイベント。複数の呼び出し元が同じライブクエリのコールバックを登録する場合、ドライバーはクエリの1つのコピーのみを監視し、登録された各コールバックを呼び出して同じ結果を返します。

サーバーがコレクションを更新するたびに、ドライバーはそのコレクションの各ライブクエリを再計算します(Meteorの今後のバージョンでは、更新時に再計算するライブクエリを制限するためのスケーリングAPIが公開されます)。 Meteorサーバーをバイパスした帯域外データベースの更新をキャッチします。

マージボックス

仕事マージボックスが(結果を組み合わせることであるaddedchangedremoved 単一のデータ・ストリームにクライアントのアクティブなパブリッシュ機能のすべてのコール)。接続されているクライアントごとに1つのマージボックスがあります。クライアントのminimongoキャッシュの完全なコピーを保持します。

サブスクリプションが1つだけの例では、マージボックスは本質的にパススルーです。しかし、より複雑なアプリは、重複する可能性のある複数のサブスクリプションを持つことができます。2つのサブスクリプションの両方が同じドキュメントに同じ属性を設定した場合、マージボックスはどの値が優先されるかを決定し、それをクライアントにのみ送信します。サブスクリプションの優先度を設定するためのAPIはまだ公開していません。現時点では、優先度は、クライアントがデータセットにサブスクライブする順序によって決定されます。クライアントが行う最初のサブスクリプションの優先度が最も高く、2番目のサブスクリプションが次に高いというように続きます。

マージボックスはクライアントの状態を保持するため、パブリッシュ関数がフィードする内容に関係なく、最小量のデータを送信して各クライアントを最新の状態に保つことができます。

アップデートで何が起こるか

これで、シナリオの準備が整いました。

1,000のクライアントが接続されています。それぞれが同じライブMongoクエリ(Somestuff.find({}))にサブスクライブされています。クエリは各クライアントで同じであるため、ドライバーは1つのライブクエリのみを実行します。1,000個のアクティブなマージボックスがあります。そして、各クライアントの機能を公開し、登録addedchangedおよび removedフィードマージボックスのいずれかにすることをそのライブのクエリに。他には何も結合ボックスに接続されていません。

まずモンゴドライバー。クライアントの1つが新しいドキュメントをに挿入するSomestuffと、再計算がトリガーされます。Mongoドライバーは、内のすべてのドキュメントのクエリを再実行Somestuffし、結果をメモリ内の以前の結果と比較して、1つの新しいドキュメントがあることを検出し、登録されている1,000個のinsertコールバックのそれぞれを呼び出します。

次に、公開機能。ここではほとんど何も起こりません。1,000のinsertコールバックのそれぞれがを呼び出してデータをマージボックスにプッシュしますadded

最後に、各マージボックスは、クライアントのキャッシュのメモリ内コピーに対してこれらの新しい属性をチェックします。どちらの場合も、値がクライアントにまだないこと、既存の値をシャドウしないことがわかります。そのため、マージボックスはDATASockJS接続でDDP メッセージをクライアントに送信し、サーバー側のメモリ内コピーを更新します。

合計CPUコストは、1つのMongoクエリを比較するためのコストに、クライアントの状態をチェックして新しいDDPメッセージペイロードを構築する1,000のマージボックスのコストを足したものです。ネットワーク上を流れる唯一のデータは、データベース内の新しいドキュメントに対応する1,000個のクライアントのそれぞれに送信される単一のJSONオブジェクトと、元の挿入を行ったクライアントからサーバーへの 1つのRPCメッセージです。

最適化

これが私たちが間違いなく計画したことです。

  • より効率的なMongoドライバー。 0.5.1でドライバー最適化して、個別のクエリごとに1つのオブザーバーのみを実行しました。

  • すべてのDBの変更がクエリの再計算をトリガーする必要はありません。いくつかの自動化された改善を行うことができますが、最善のアプローチは、再実行する必要があるクエリを開発者が指定できるAPIです。たとえば、1つのチャットルームにメッセージを挿入しても、2番目のルームのメッセージに対するライブクエリが無効にならないことは開発者にとって明らかです。

  • Mongoドライバー、パブリッシュ関数、マージボックスは、同じプロセスで実行する必要はなく、同じマシンで実行する必要もありません。一部のアプリケーションは複雑なライブクエリを実行し、データベースを監視するためにより多くのCPUを必要とします。他のユーザーは、わずかなクエリしか持っていません(ブログエンジンを想像してください)が、おそらく多くの接続されたクライアントがあります。これらは、マージボックスのためにより多くのCPUを必要とします。これらのコンポーネントを分離すると、各部分を個別にスケーリングできます。

  • 多くのデータベースは、行が更新されたときに起動し、古い行と新しい行を提供するトリガーをサポートしています。その機能により、データベースドライバーは変更をポーリングする代わりにトリガーを登録できます。


Meteor.publishを使用して非カーソルデータを公開する方法の例はありますか?回答で言及されているレガシーREST APIの結果などですか?
Tony

@Tony:ドキュメントにあります。部屋数の例を確認してください。
Mitar 2013

リリース0.7、0.7.1、0.7.2では、Meteorはほとんどのクエリ(例外はskip$nearおよび$whereクエリを含む)でOpLog Observe Driverに切り替えられ、CPU負荷、ネットワーク帯域幅ではるかに効率的であり、アプリケーションのスケールアウトを可能にすることに注意してください。サーバー。
imslavko 14年

すべてのユーザーに同じデータが表示されるわけではない場合はどうでしょうか。1.彼らはさまざまなトピックにサブスクライブしました。それらは異なる役割を持っているため、同じメイントピック内で、それらに到達するはずのないメッセージがいくつかあります。
tgkprog 2014年

キャッシュの無効化に関する@debergalis、おそらく私の論文vanisoft.pl/~lopuszanski/public/cache_invalidation.pdfの価値あるアイデア
qbolec

29

私の経験から、Meteorで膨大なコレクションを共有しながら多くのクライアントを使用することは、バージョン0.7.0.1以降、本質的に機能しません。その理由を説明しようと思います。

上記の投稿とhttps://github.com/meteor/meteor/issues/1821にも記載されているように、Meteorサーバーは各クライアントの公開データのコピーをマージボックスに保持する必要があります。これにより、Meteorマジックが実行されますが、大きな共有データベースがノードプロセスのメモリに繰り返し保持されます。(meteorにコレクションが静的である(変更されない)と伝える方法はありますか?)などの静的コレクションに可能な最適化を使用する場合でも、ノードプロセスのCPUとメモリの使用に大きな問題が発生しました。

私たちのケースでは、完全に静的な15kドキュメントのコレクションを各クライアントに公開していました。問題は、接続時にこれらのドキュメントをクライアントのマージボックス(メモリ内)にコピーすると、基本的にノードプロセスのCPU使用率がほぼ1秒間100%増加し、メモリの使用量が大幅に増えることです。これは本質的にスケーラブルではありません。接続しているクライアントはサーバーをひざまずかせ(同時接続は相互にブロックします)、クライアントの数に比例してメモリ使用量が増加します。私たちのケースでは、転送された生データが約5MBであるにもかかわらず、各クライアントが追加で最大60MBのメモリを使用していました。

私たちの場合、コレクションは静的だったため、.jsonnginxによってgzip圧縮されたファイルとしてすべてのドキュメントを送信し、匿名のコレクションにロードすることで、この問題を解決しました。追加のCPUを使用せずに、データの転送は約1MBのみになりました。またはノードプロセスのメモリとロード時間の高速化。このコレクションに対するすべての操作_idは、サーバー上の非常に小さなパブリケーションのsを使用して行われたため、Meteorのほとんどの利点を維持できます。これにより、アプリをより多くのクライアントに拡張できました。さらに、アプリはほとんど読み取り専用であるため、各ノードインスタンスがシングルスレッドであるため、負荷分散を使用して(単一のMongoを使用して)nginxの背後で複数のMeteorインスタンスを実行することにより、スケーラビリティをさらに向上させました。

ただし、複数のクライアント間で書き込み可能な大規模なコレクションを共有するという問題は、Meteorが解決する必要のあるエンジニアリング上の問題です。クライアントごとにすべてのコピーを保持するよりも良い方法がおそらくありますが、そのためには、分散システムの問題として真剣に考える必要があります。大量のCPUとメモリの使用に関する現在の問題は、スケーリングされません。


@Harry oplogはこの状況では重要ではありません。データは静的でした。
Andrew Mao 14

サーバー側のminimongoコピーの差分を作成しないのはなぜですか?多分それは1.0ですべて変更されましたか?私は、彼らが希望したい同じI、それは同様であろうコールバックしていることにも機能している通常意味(私はそれは同様に、潜在的に異なるが格納されているものですしていること以下だ場合。)
MistereeDevlord

@MistereeDevlord変更の差分とクライアントデータのキャッシュは、現在個別です。全員が同じデータを持ち、1つの差分のみが必要な場合でも、サーバーがそれらを同じように処理できないため、クライアントごとのキャッシュは異なります。これは確かに既存の実装よりも賢く行うことができます。
Andrew Mao

@AndrewMao gzipで圧縮されたファイルがクライアントに送信されるときに、そのファイルが保護されていることをどのように確認しますか。つまり、ログインしたクライアントだけがファイルにアクセスできますか?
FullStack、2015年

4

この質問に答えるために使用できる実験:

  1. テスト流星をインストールします。 meteor create --example todos
  2. Webkitインスペクター(WKI)で実行します。
  3. ネットワーク上を移動するXHRメッセージの内容を調べます。
  4. コレクション全体がネットワーク上を移動していないことを確認してください。

WKIの使い方のヒントについては、この記事をご覧ください。それは少し時代遅れですが、特にこの質問に関しては、ほとんどがまだ有効です。


2
ポーリングメカニズムの説明:eventedmind.com/posts/meteor-liveresultsset
cmather

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.