100

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

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

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

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

何が起こるか:

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

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

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

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

4

4 に答える 4

119

簡単に言うと、新しいデータだけがネットワークに送信されます。仕組みは次のとおりです。

サブスクリプションを管理するMeteorサーバーには、次の3つの重要な部分があります。サブスクリプションが提供するデータのロジックを定義する公開機能。データベースの変更を監視するMongoドライバー。マージボックスは、クライアントのアクティブなサブスクリプションをすべて組み合わせて、ネットワーク経由でクライアントに送信します。

公開機能

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

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

一方、データベースクエリの公開に限定されません。たとえば、内部のデバイスからGPS位置を読み取るMeteor.setInterval、または別のWebサービスからレガシーRESTAPIをポーリングする公開関数を作成できます。addedこのような場合、低レベルのDDP APIchangedを呼び出すことで、マージボックスに変更を加えます。removed

Mongoドライバー

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

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

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

マージボックス

マージボックスの役割は、クライアントのすべてのアクティブな公開関数の結果(addedchangedおよびremoved 呼び出し)を単一のデータストリームに結合することです。接続されているクライアントごとに1つのマージボックスがあります。クライアントのminimongoキャッシュの完全なコピーを保持します。

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

マージボックスはクライアントの状態を保持するため、公開関数がクライアントにフィードする内容に関係なく、各クライアントを最新の状態に保つために最小限のデータを送信できます。

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

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

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

まず、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を必要とします。これらのコンポーネントを分離すると、各ピースを個別にスケーリングできます。

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

于 2012-12-13T19:37:01.560 に答える
29

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

上記の投稿とhttps://github.com/meteor/meteor/issues/1821で説明されているように、Meteor サーバーは各クライアントの公開データのコピーをマージ ボックスに保持する必要があります。これが Meteor の魔法を可能にしますが、大規模な共有データベースがノード プロセスのメモリに繰り返し保持されることにもなります。( Is there a way to tell meteor a collection is static (will never change)? のような静的コレクションの可能な最適化を使用した場合でも、ノード プロセスの CPU とメモリの使用量に大きな問題が発生しました。

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

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

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

于 2014-02-17T17:28:32.087 に答える
4

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

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

WKI の使用方法に関するヒントについては、この記事を参照してください。少し古くなっていますが、特にこの質問では、ほとんどがまだ有効です。

于 2012-11-18T02:59:23.603 に答える
3

これはまだ 1 年経っているので、「Meteor 1.0」以前の知識だと思います。また状況が変わったのでしょうか? 私はまだこれを調べています。 http://meteorhacks.com/does-meteor-scale.html は、「Meteor のスケーリング方法」につながります。記事 http://meteorhacks.com/how-to-scale-meteor.html

于 2014-12-05T00:10:23.583 に答える