1

次のことを行うノードサーバーがあります。

外部サーバーに URL のリストがあり、それを URLServer と呼びます。ユーザーが NODE サーバーにアクセスすると、ノード サーバーは URLServer にリクエストを送信し、たとえば 20 個の URL のリストを取得します。20 個の URL を取得したらすぐに、ノード サーバーにこれらの URL のそれぞれのタイトルを取得させたいと考えています。つまり、URL を取得して DOM を作成し、タイトルを抽出します。他のデータも取得します。したがって、これはそれが行われなければならない方法です。それが完了したら、URL のタイトルと URL を内部メモリやデータベースに保存したいと考えています。そのため、URL キャッシュとタイトル キャッシュがあります (常に URL を取得する必要はありません)。

私はこのようなものを持っています: if(URL-cache is empty) URLServer から URL を取得し、これらの URL をキャッシュします

次に、これらの URL のそれぞれをチェックして、タイトルがキャッシュにあるかどうかを確認したいので、各 URL に対して、title-cache[URL] の場合、そうでない場合はサイトを取得し、DOM を作成し、タイトルとその他のデータを抽出してキャッシュします。

これは 1 人のユーザーにはうまく機能しますが、サーバーに大きな負荷をかけようとすると、サーバーがハングします。サーバーがハングする理由は次のとおりです。

ユーザー 1 の要求 - キャッシュを空にする - URL を取得し、完了したら URL のコンテンツを取得します ユーザー 2 の要求 - ユーザー 1 の要求がまだ完了していないため、このユーザーにはキャッシュがまだ空に見えます!!! したがって、ユーザー 2 は、URL とそれぞれのコンテンツのフェッチを再度強制します。ユーザー 3 のリクエスト - ユーザー 1 とユーザー 2 のリクエストがまだ完了していないため、同じ問題が発生します...

したがって、取得する必要がある 10 個の URL があると仮定すると、URL ごとに 1 つずつ 10 個の接続を開いてからデータをキャッシュする代わりに、20 人のユーザーがまったく同時にサーバーにアクセスしている場合、200 個の接続を開くことになります (各ユーザー10 接続を開きます)。

ユーザー X (X>1) がこれらのイベントを発生させないようにするにはどうすればよいですか? 基本的に、サーバーにゲートを閉じて、すべてのユーザーにキャッシュが読み込まれるまで待機するように依頼し、キャッシュが読み込まれたらゲートを開くようにしたいのですが、これを行う方法はありますか?

4

2 に答える 2

3

これは、EventEmitterクラスを使用して実行できます。EventEmitterを設定します

    var events = require('events');
    var eventEmitter = new events.EventEmitter();

次に、着信リクエストを処理します

    // here you check for url in cache with your own logic
    if(weHaveUrl){
      // Respond directly
    } else {
      // Add one time event watcher for that url
      eventEmitter.once('url-' + url, function(data){
        // We now have data so respond
      });
      // Initiate search
      searchUrl(url);
    }

そして、検索関数をラップしてイベントを発行します

    var urlSearchList = [];
    function searchUrl(url){
      // We check in case we are already looking for the data
      if(urlSearchList.indexOf(url) === -1){
        // Append url to list so we won't start a second search
        urlSearchList.push(url);

        // Your logic for searching url data
        // Once recieved we emit the event
        eventEmitter.emit('url-' + url);
        // And optionally remove from search array 
        //  if we want to repeat the search at some point
        urlSearchList.splice(urlSearchList.indexOf(url));
      }
    }

このメソッドは、結果がキャッシュにある場合はすぐにリクエストに応答するか、検索からの結果を待ってから結果を返します。

どの検索が開始されたかを記録しているので、同じURLの検索を何度も開始することはなく、結果が利用可能になるとすぐにすべてのリクエストが応答を受け取ります。

于 2012-09-19T14:24:58.767 に答える
1

このイベントを回避する最も簡単な方法 (ちなみに、これは "雷の群れの問題" と呼ばれています) は、どのユーザーにもfetchURLsコードを実行させないことです。代わりに、キャッシュ チェックが失敗した場合は、ジョブ キューにジョブを追加して、このデータを更新します。次に、「申し訳ありませんが、そのデータは現在ありません。取得するまでお待ちください」という内容のメッセージを返します。次に、データのエンドポイントをポーリングするだけで、データがキャッシュに入れられると、すべての設定が完了し、準備が整います。

ジョブが 100 人のユーザーによってキューに送信されるのを防ぐために、別のグローバルに使用可能なデータ構造にフラグを追加します (ジョブ キューに使用しているのと同じものである可能性がありますが、必ずしもそうとは限りません)。そのキャッシュ キーのフラグの存在を確認するキャッシュ ミス チェックが発生し、存在しない場合は、フラグを設定し、ジョブをジョブ キューに送信します。擬似コード:

if url not in cache:
    if url not in jobLocks:
        jobLocks.add(url)
        jobQueue.add("fetchURLs", data=url)

    return "Please wait while we fetch your data"

else:
    return cache[url]

キャッシュ内のデータが古くなった場合、同じプロセスを使用して、更新時に群れが激しくなるのを避けることができます。データを削除してから再度取得する代わりに、古いデータをサーバーに送り、ジョブをキューに入れてキャッシュを更新します。

于 2012-09-19T02:24:51.590 に答える