Facebook、Gmailはどのようにしてリアルタイム通知を送信しますか?


269

私はこのトピックに関するいくつかの投稿を読んだことがあり、答えは彗星、逆Ajax、httpストリーミング、サーバープッシュなどです。

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

GMail Chatはクライアントとのやり取りなしでどのようにAJAXリクエストを実行できますか?

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

サーバー側とクライアント側の両方で実行する必要があることはわかっています。PHPおよびJavascriptのサンプルコードはありますか?

回答:


428

Facebookがこれを行う方法はかなり興味深いものです。

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

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

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

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

実装:

サーバ側:

PHPにはコメットライブラリの実装がいくつかあるように見えますが、正直なところ、次の疑似コードのように非常に単純です。

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

echo json_encode(get_events());
  • has_event_happened関数は、イベントテーブルなどで何かが発生したかどうかを確認し、get_events関数はテーブルの新しい行のリストを返しますか?問題の状況によって異なります。

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

クライアント側:

コメットの相互作用を行うための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
});

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


2
それは非常に素晴らしく詳細な説明です。ありがとうございました。それを実装する多くの方法の1つに対するサンプルコードはありますか?
ビリー

45
PHPを適切にスケーリングできない言語/プラットフォームとしてラベル付けすることは、必ずしも正しいとは限りません。非常に大規模なシステムの開発に使用できます。Facebookを見てください。開発者が正しく行うと、スケーリングされます。そうでない場合は、スケーリングされません。特定のWebプラットフォームを使用しても、スケーラビリティが保証されるわけではありません。ああ、そしてまた、質問はPHPを求めました。
Alistair Evans

5
@Kazar:「FacebookがPHPを使用する」は少し誤解を招きやすい-最後に聞いたところによると、PHPが十分に機能していないため、PHPをC ++に変換するという明確な目的でHipHopを開発した。
cHao

14
@cHao:当たり前のことですが、この回答はFacebookがヒップホップを使い始める前の2009年に書かれました。当時、facebookはまだそれ自体でphpを使用する非常に大規模なシステムでした。
Alistair Evans、

6
したがって、この手法は、接続を常に開いたままにして、サーバーに一定のストレスをかけ続けることです。平均的なWebサーバーの一般的な同時接続数は約200ですが、同時にオンラインになっているFacebookユーザーの数ははるかに多くなります。彼らはそれをどのように行うのですか?
ポール

43

更新

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


最近同じ問題があり、この問題について調査しました。

与えられた解決策はロングポーリングと呼ばれ、それを正しく使用するには、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は、何も起こらない間にスレッドを解放して他のことを行うためのものです。eventsJSON形式のイベントの辞書(またはあなたが好むかもしれない他のデータ構造)を返します(または任意の他のあなたが好みます)。

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

解決

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

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

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

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

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


1は他の代替品だと思いますか、それとも同じプロジェクトで両方のテクノロジーが必要ですか?
tq 2013年

APEとNodeJSの場合は、どちらか1つを選択できます。定期的なAJAXリクエストを意味し、私が提案したリクエストの場合、私のソリューションは、ソケットのサポートがない場合にajaxのリクエストにフォールバックすることがあります(socket.ioのドキュメントを参照)。どちらの場合も、必要なソリューションは1つだけです。
Walter Macambira 2013年

ちょっとウォルター、私のサイトの1つであなたの提案を使用したいと思います。私がソケットサーバーをどこで手に入れることができるか知っていますか?ありがとう!
プロゴ2014

1
あなたはそれを実装することができます。ノードはそれを本当に簡単にします。
Walter Macambira 2014

検出する方法hasTimedOut()
Mobasher Fasihy、2015

18

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

下の図では、「チャネルクラスタ」という語句は「彗星サーバー」を意味しています。

システム概要

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

あなたは試すことができicomet、libeventで構築されたC1000K C ++彗星サーバーを。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をサポートしています。


この画像はシナリオを非常によく説明しています。実例が与えられていれば素晴らしいと思います。たとえば、ある人が友達とチャットボックスを開いた(開始した)場合、どうなりますか?Facebookがこの特定の会話をどのように調整し、メッセージを両端にプッシュするのですか?(単なる推測:アプリケーションプログラムがソケットを開いて両方のクライアントアドレスをバインドし、メッセージがボックスに書き込まれるたびにリスニングと書き込みを続けることを想像するだけです)
edam

5

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

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

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


3
さらに、GoogleはAndroid向けのGCMサービスを使用しており、プッシュメッセージサービスを実装するために開発者が使用できます。developer.android.com/google/gcm/index.html 回答が役に立った場合は受け入れてください。
abhi

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