278

私はこのトピックに関するいくつかの投稿を読みましたが、答えはコメット、リバースajax、httpストリーミング、サーバープッシュなどです。

Gmailでの受信メール通知はどのように機能しますか?

Gmailチャットはどのようにしてクライアントとの対話なしでAJAXリクエストを行うことができますか?

非常に簡単な例を書くために従うことができるコード参照があるかどうか知りたいです。多くの投稿やウェブサイトは、テクノロジーについて話しているだけです。完全なサンプルコードを見つけるのは難しいです。また、Hidden IFrame、XMLHttpRequestなど、多くのメソッドを使用して彗星を実装できるようです。私の意見では、XMLHttpRequestを使用する方が良い選択です。さまざまな方法の長所と短所についてどう思いますか?Gmailはどちらを使用しますか?

サーバー側とクライアント側の両方でそれを行う必要があることを私は知っています。PHPとJavascriptのサンプルコードはありますか?

4

5 に答える 5

438

Facebookがこれを行う方法は非常に興味深いものです。

このような通知を行う一般的な方法は、サーバー上のスクリプトを(AJAXを使用して)特定の間隔(おそらく数秒ごと)にポーリングして、何かが発生したかどうかを確認することです。ただし、これはかなりネットワークを集中的に使用する可能性があり、何も起こらなかったため、無意味な要求を行うことがよくあります。

Facebookが行う方法は、間隔を置いてポーリングするのではなく、コメットアプローチを使用することです。あるポーリングが完了するとすぐに、別のポーリングを発行します。ただし、サーバー上のスクリプトへの各要求には非常に長いタイムアウトがあり、サーバーは何かが発生した場合にのみ要求に応答します。FacebookでFirebugの[コンソール]タブを表示すると、スクリプトへのリクエストに数分かかる可能性があるため、これが発生していることがわかります。この方法では、リクエストの数と送信頻度の両方がすぐに削減されるため、非常に独創的です。これで、サーバーがイベントを「起動」できるようにするイベントフレームワークが効果的に作成されました。

この背後にあるのは、これらの投票から返された実際のコンテンツに関しては、JSON応答であり、イベントのリストとその情報が表示されます。ただし、縮小されているため、少し読みにくいです。

実際のテクノロジーに関しては、AJAXがここに行く方法です。これは、リクエストのタイムアウトやその他の多くのことを制御できるためです。jQueryを使用してAJAXを実行することをお勧めします(ここではスタックオーバーフローの決まり文句です)。これにより、相互互換性の問題の多くが解消されます。PHPに関しては、PHPスクリプトでイベントログデータベーステーブルをポーリングし、何かが発生したときにのみクライアントに戻ることができますか?これを実装する方法はたくさんあると思います。

実装:

サーバ側:

PHPにはcometライブラリの実装がいくつかあるように見えますが、正直なところ、これは非常に単純で、おそらく次の擬似コードのようなものです。

while(!has_event_happened()) {
   sleep(5);
}

echo json_encode(get_events());
  • has_event_happened関数は、イベントテーブルなどで何かが発生したかどうかをチェックするだけで、get_events関数はテーブル内の新しい行のリストを返しますか?本当に問題の文脈に依存します。

  • PHPの最大実行時間を変更することを忘れないでください。変更しないと、早期にタイムアウトします。

クライアント側:

Cometインタラクションを行うためのjQueryプラグインを見てください。

  • プロジェクトのホームページ: http: //plugins.jquery.com/project/Comet
  • Googleコード:https : //code.google.com/archive/p/jquerycomet/-Subversionリポジトリにある種の使用例があるようです。

とは言うものの、プラグインはかなり複雑になるようです。クライアントでは非常に単純で、おそらく(jQueryを使用すると)次のようになります。

function doPoll() {
   $.get("events.php", {}, function(result) {
      $.each(result.events, function(event) { //iterate over the events
          //do something with your event
      });
      doPoll(); 
      //this effectively causes the poll to run again as
      //soon as the response comes back
   }, 'json'); 
}

$(document).ready(function() {
    $.ajaxSetup({
       timeout: 1000*60//set a global AJAX timeout of a minute
    });
    doPoll(); // do the first poll
});

全体は、既存のアーキテクチャをどのように組み合わせるかに大きく依存します。

于 2009-07-06T10:58:45.760 に答える
47

アップデート

私はこれについて賛成票を受け取り続けているので、この答えは4歳であることを覚えておくのは合理的だと思います。Webは非常に速いペースで成長しているので、この答えに注意してください。


私は最近同じ問題を抱えていて、その主題について調査しました。

与えられた解決策はロングポーリングと呼ばれ、それを正しく使用するには、AJAXリクエストに「大きな」タイムアウトがあることを確認し、現在の終了(タイムアウト、エラー、または成功)後に常にこのリクエストを行う必要があります。

ロングポーリング-クライアント

ここでは、コードを短くするために、jQueryを使用します。

function pollTask() { 

    $.ajax({

        url: '/api/Polling',
        async: true,            // by default, it's async, but...
        dataType: 'json',       // or the dataType you are working with
        timeout: 10000,          // IMPORTANT! this is a 10 seconds timeout
        cache: false

    }).done(function (eventList) {  

       // Handle your data here
       var data;
       for (var eventName in eventList) {

            data = eventList[eventName];
            dispatcher.handle(eventName, data); // handle the `eventName` with `data`

       }

    }).always(pollTask);

}

( jQuery docsから)次のことを覚えておくことが重要です。

jQuery 1.4.x以下では、リクエストがタイムアウトした場合、XMLHttpRequestオブジェクトは無効な状態になります。オブジェクトメンバーにアクセスすると、例外がスローされる場合があります。Firefox 3.0以降のみで、スクリプトとJSONPリクエストをタイムアウトでキャンセルすることはできません。スクリプトは、タイムアウト期間の後に到着した場合でも実行されます。

ロングポーリング-サーバー

特定の言語ではありませんが、次のようになります。

function handleRequest () {  

     while (!anythingHappened() || hasTimedOut()) { sleep(2); }

     return events();

} 

ここでhasTimedOutは、コードが永久に待機しないことを確認し、anythingHappenedイベントが発生したかどうかを確認します。これsleepは、何も起こらないうちにスレッ​​ドを解放して他のことを行うためのものです。はevents、イベントのディクショナリ(または任意のデータ構造)をJSON形式(またはその他の任意のデータ構造)で返します。

それは確かに問題を解決しますが、私が調査したときのようにスケーラビリティとパフォーマンスが心配な場合は、私が見つけた別の解決策を検討するかもしれません。

解決

ソケットを使用してください!

クライアント側では、互換性の問題を回避するために、socket.ioを使用します。ソケットを直接使用しようとし、ソケットが使用できない場合は他のソリューションにフォールバックします。

サーバー側では、NodeJSを使用してサーバーを作成します(ここの例)。クライアントは、サーバーで作成されたこのチャネル(オブザーバー)にサブスクライブします。通知を送信する必要がある場合は常に、このチャネルで公開され、添え字(クライアント)に通知されます。

このソリューションが気に入らない場合は、APE(Ajax Push Engine)を試してください。

私が助けてくれたことを願っています。

于 2013-04-25T00:11:58.613 に答える
19

Facebookのメッセージングシステムに関するスライドショーによると、Facebookはコメットテクノロジーを使用してメッセージをWebブラウザに「プッシュ」します。Facebookのcometサーバーは、オープンソースのErlangWebサーバーmochiweb上に構築されています。

下の図で、「チャネルクラスター」というフレーズは「cometサーバー」を意味します。

システム概要

他の多くの大きなWebサイトは、すべての企業のニーズに違いがあるため、独自のコメットサーバーを構築しています。しかし、オープンソースのコメットサーバー上に独自のコメットサーバーを構築することは良いアプローチです。

libeventで構築されたC1000KC++コメットサーバーであるicomet試すことができます。icometはJavaScriptライブラリも提供しており、次のように簡単に使用できます。

var comet = new iComet({
    sign_url: 'http://' + app_host + '/sign?obj=' + obj,
    sub_url: 'http://' + icomet_host + '/sub',
    callback: function(msg){
        // on server push
        alert(msg.content);
    }
});

icometは、Safari(iOS、Mac)、IE(Windows)、Firefox、Chromeなど、さまざまなブラウザとOSをサポートしています。

于 2013-10-01T02:38:43.870 に答える
5

FacebookはHTTPの代わりにMQTTを使用します。プッシュはポーリングよりも優れています。HTTPを介してサーバーを継続的にポーリングする必要がありますが、MQTTを介してサーバーはメッセージをクライアントにプッシュします。

MQTTとHTTPの比較:http ://www.youtube.com/watch?v = -KNPXPmx88E

注:私の答えはモバイルデバイスに最適です。

于 2013-06-01T16:45:25.700 に答える
5

長いポーリングに関する重要な問題の1つは、エラー処理です。エラーには2つのタイプがあります。

  1. リクエストがタイムアウトする可能性があります。その場合、クライアントはすぐに接続を再確立する必要があります。これは、メッセージが到着していない場合の長いポーリングでの通常のイベントです。

  2. ネットワークエラーまたは実行エラー。これは実際のエラーであり、クライアントはサーバーがオンラインに戻るのを適切に受け入れて待機する必要があります。

主な問題は、エラーハンドラがタイプ2エラーに対してもすぐに接続を再確立した場合、クライアントがサーバーをDOSすることです。

コードサンプルの両方の答えはこれを見逃しています。

function longPoll() { 
        var shouldDelay = false;

        $.ajax({
            url: 'poll.php',
            async: true,            // by default, it's async, but...
            dataType: 'json',       // or the dataType you are working with
            timeout: 10000,          // IMPORTANT! this is a 10 seconds timeout
            cache: false

        }).done(function (data, textStatus, jqXHR) {
             // do something with data...

        }).fail(function (jqXHR, textStatus, errorThrown ) {
            shouldDelay = textStatus !== "timeout";

        }).always(function() {
            // in case of network error. throttle otherwise we DOS ourselves. If it was a timeout, its normal operation. go again.
            var delay = shouldDelay ? 10000: 0;
            window.setTimeout(longPoll, delay);
        });
}
longPoll(); //fire first handler
于 2016-01-12T20:33:29.530 に答える