Wie sendet Facebook, Google Mail die Echtzeitbenachrichtigung?

269

Ich habe einige Beiträge zu diesem Thema gelesen und die Antworten lauten Komet, Reverse Ajax, http-Streaming, Server-Push usw.

Wie funktioniert die Benachrichtigung über eingehende E-Mails in Google Mail?

Wie kann GMail Chat AJAX-Anfragen ohne Client-Interaktion stellen?

Ich würde gerne wissen, ob es Code-Referenzen gibt, denen ich folgen kann, um ein sehr einfaches Beispiel zu schreiben. Viele Beiträge oder Websites sprechen nur über die Technologie. Es ist schwierig, einen vollständigen Beispielcode zu finden. Es scheint auch, dass viele Methoden verwendet werden können, um den Kometen zu implementieren, z. B. Hidden IFrame, XMLHttpRequest. Meiner Meinung nach ist die Verwendung von XMLHttpRequest die bessere Wahl. Was halten Sie von den Vor- und Nachteilen verschiedener Methoden? Welches verwendet Google Mail?

Ich weiß, dass dies sowohl auf der Server- als auch auf der Client-Seite erforderlich ist. Gibt es PHP- und Javascript-Beispielcode?

Billy
quelle

Antworten:

428

Die Art und Weise, wie Facebook dies tut, ist ziemlich interessant.

Eine übliche Methode für solche Benachrichtigungen besteht darin, ein Skript auf dem Server (unter Verwendung von AJAX) in einem bestimmten Intervall (möglicherweise alle paar Sekunden) abzufragen, um zu überprüfen, ob etwas passiert ist. Dies kann jedoch ziemlich netzwerkintensiv sein, und Sie stellen häufig sinnlose Anfragen, da nichts passiert ist.

Bei Facebook wird der Kometenansatz verwendet, anstatt in einem Intervall abzufragen. Sobald eine Umfrage abgeschlossen ist, wird eine andere ausgegeben. Jede Anforderung an das Skript auf dem Server hat jedoch ein extrem langes Zeitlimit, und der Server antwortet auf die Anforderung erst, wenn etwas passiert ist. Sie können dies beobachten, wenn Sie auf Facebook die Registerkarte "Konsole" von Firebug aufrufen und Anfragen an ein Skript möglicherweise Minuten dauern. Es ist wirklich ziemlich genial, da diese Methode sowohl die Anzahl der Anfragen als auch die Häufigkeit, mit der Sie sie senden müssen, sofort reduziert. Sie haben jetzt effektiv ein Ereignis-Framework, mit dem der Server Ereignisse auslösen kann.

Dahinter steht in Bezug auf den tatsächlichen Inhalt, der aus diesen Umfragen zurückgegeben wurde, eine JSON-Antwort mit einer scheinbaren Liste von Ereignissen und Informationen darüber. Es ist zwar verkleinert, daher etwas schwer zu lesen.

In Bezug auf die aktuelle Technologie ist AJAX der richtige Weg, da Sie Anforderungszeitlimits und viele andere Dinge steuern können. Ich würde empfehlen (Stack Overflow-Klischee hier), jQuery für AJAX zu verwenden, da dadurch viele Probleme mit der Kompatibilität behoben werden. In Bezug auf PHP könnten Sie einfach eine Ereignisprotokoll-Datenbanktabelle in Ihrem PHP-Skript abfragen und nur dann zum Client zurückkehren, wenn etwas passiert? Ich gehe davon aus, dass es viele Möglichkeiten gibt, dies umzusetzen.

Implementierung:

Serverseite:

Es scheint einige Implementierungen von Kometenbibliotheken in PHP zu geben, aber um ehrlich zu sein, ist es wirklich sehr einfach, so etwas wie der folgende Pseudocode:

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

echo json_encode(get_events());
  • Die Funktion has_event_happened prüft nur, ob in einer Ereignistabelle oder etwas anderem etwas passiert ist, und die Funktion get_events gibt dann eine Liste der neuen Zeilen in der Tabelle zurück. Kommt wirklich auf den Kontext des Problems an.

  • Vergessen Sie nicht, die maximale Ausführungszeit von PHP zu ändern, da sonst die Zeit abläuft!

Kundenseite:

Schauen Sie sich das jQuery-Plugin für die Comet-Interaktion an:

Das Plugin scheint jedoch einiges an Komplexität hinzuzufügen. Es ist auf dem Client wirklich sehr einfach, vielleicht (mit jQuery) so etwas wie:

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
});

Das Ganze hängt stark davon ab, wie Ihre vorhandene Architektur zusammengesetzt ist.

Alistair Evans
quelle
2
Es ist eine sehr schöne und detaillierte Erklärung. Danke dir. Haben Sie einen Beispielcode für eine der vielen Möglichkeiten, dies zu implementieren?
Billy
45
Ich denke, PHP als eine Sprache / Plattform zu kennzeichnen, die nicht gut skaliert werden kann, ist nicht unbedingt wahr. Es kann verwendet werden, um extrem große Systeme zu entwickeln. Schau dir Facebook an. Wenn der Entwickler es richtig macht, wird es skaliert, wenn nicht, wird es nicht. Die Verwendung einer bestimmten Webplattform ist keine Garantie für die Skalierbarkeit. Oh, und auch die Frage hat nach PHP gefragt.
Alistair Evans
5
@Kazar: "Facebook verwendet PHP" ist etwas irreführend - zuletzt habe ich gehört, dass sie HipHop für den ausdrücklichen Zweck der Konvertierung von PHP in C ++ entwickelt haben, da PHP nicht gut genug lief.
CHao
14
@cHao: Es ist ein fairer Punkt, aber diese Antwort wurde 2009 geschrieben, bevor Facebook anfing, HipHop zu verwenden. Zu der Zeit war Facebook noch ein sehr großes System, das PHP alleine verwendete.
Alistair Evans
6
Die Technik besteht also darin, eine Verbindung ständig offen zu halten, wodurch ein Server ständig belastet wird. Eine typische Anzahl gleichzeitiger Verbindungen für einen durchschnittlichen Webserver beträgt etwa 200, aber die Anzahl der Facebook-Benutzer, die gleichzeitig online sind, ist viel größer. Wie haben sie das gemacht?
Paul
43

Aktualisieren

Da ich weiterhin positive Stimmen dazu erhalte, halte ich es für vernünftig, mich daran zu erinnern, dass diese Antwort 4 Jahre alt ist. Das Web ist sehr schnell gewachsen. Bitte beachten Sie diese Antwort.


Ich hatte kürzlich das gleiche Problem und recherchierte über das Thema.

Die angegebene Lösung wird als langes Polling bezeichnet. Um sie korrekt zu verwenden, müssen Sie sicherstellen, dass Ihre AJAX-Anforderung ein "großes" Zeitlimit aufweist, und diese Anforderung immer nach dem Ende des aktuellen Zeitraums (Zeitlimit, Fehler oder Erfolg) stellen.

Lange Umfrage - Client

Um den Code kurz zu halten, verwende ich hier 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);

}

Es ist wichtig, sich daran zu erinnern (aus jQuery-Dokumenten ):

In jQuery 1.4.x und darunter befindet sich das XMLHttpRequest-Objekt in einem ungültigen Zustand, wenn die Anforderung abgelaufen ist. Der Zugriff auf Objektmitglieder kann eine Ausnahme auslösen. Nur in Firefox 3.0+ können Skript- und JSONP-Anforderungen nicht durch eine Zeitüberschreitung abgebrochen werden. Das Skript wird auch dann ausgeführt, wenn es nach Ablauf der Zeitspanne eintrifft.

Lange Abfrage - Server

Es ist nicht in einer bestimmten Sprache, aber es wäre ungefähr so:

function handleRequest () {  

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

     return events();

} 

Hier hasTimedOutwird sicherstellen , dass Ihr Code nicht ewig warten, und anythingHappenedwird prüfen , ob jeder Fall geschieht. Das sleepist für die Freigabe Ihres Threads, um andere Dinge zu tun, während nichts passiert. Das eventsgibt ein Wörterbuch mit Ereignissen (oder einer anderen von Ihnen bevorzugten Datenstruktur) im JSON-Format (oder einer anderen von Ihnen bevorzugten) zurück.

Es löst sicherlich das Problem, aber wenn Sie sich Sorgen über Skalierbarkeit und Leistung machen, wie ich es bei der Recherche war, könnten Sie eine andere Lösung in Betracht ziehen, die ich gefunden habe.

Lösung

Verwenden Sie Steckdosen!

Verwenden Sie auf der Clientseite socket.io , um Kompatibilitätsprobleme zu vermeiden . Es wird versucht, Sockets direkt zu verwenden und auf andere Lösungen zurückzugreifen, wenn keine Sockets verfügbar sind.

Erstellen Sie auf der Serverseite einen Server mit NodeJS (Beispiel hier ). Der Client abonniert diesen mit dem Server erstellten Kanal (Beobachter). Wann immer eine Benachrichtigung gesendet werden muss, wird sie in diesem Kanal veröffentlicht und der Abonnent (Client) wird benachrichtigt.

Wenn Ihnen diese Lösung nicht gefällt, versuchen Sie es mit APE ( Ajax Push Engine ).

Hoffe ich habe geholfen.

Walter Macambira
quelle
Denken Sie, dass 1 ein Ersatz für das andere ist oder dass beide Technologien im selben Projekt benötigt werden?
tq
Wenn Sie APE und NodeJS meinen, können Sie eine davon auswählen. Wenn Sie regelmäßige AJAX-Anfragen und die von mir vorgeschlagene meinen, kann meine Lösung auf die Ajax-Anfrage zurückgreifen, wenn keine Socket-Unterstützung vorhanden ist (siehe socket.io-Dokumente). In beiden Fällen benötigen Sie nur eine Lösung.
Walter Macambira
Hey Walter, ich möchte Ihren Vorschlag auf einer meiner Websites verwenden. Wissen Sie, wo ich einen Sockets-Server bekommen kann? Vielen Dank!
Progo
1
Sie können es implementieren. Node macht es wirklich einfach.
Walter Macambira
Wie zu erkennen hasTimedOut()?
Mobasher Fasihy
18

Laut einer Diashow über das Messaging-System von Facebook verwendet Facebook die Kometentechnologie, um Nachrichten an Webbrowser zu "pushen". Der Kometenserver von Facebook basiert auf dem Open-Source-Erlang-Webserver Mochiweb.

In der Abbildung unten bedeutet der Ausdruck "Kanalcluster" "Kometenserver".

Systemübersicht

Viele andere große Websites bauen ihren eigenen Kometenserver, da es Unterschiede zwischen den Anforderungen jedes Unternehmens gibt. Der Aufbau eines eigenen Kometenservers auf einem Open-Source-Kometenserver ist jedoch ein guter Ansatz.

Sie können icomet ausprobieren , einen C1000K C ++ - Kometenserver, der mit libevent erstellt wurde. icomet bietet auch eine JavaScript-Bibliothek, die so einfach zu bedienen ist wie:

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 unterstützt eine Vielzahl von Browsern und Betriebssystemen, darunter Safari (iOS, Mac), IEs (Windows), Firefox, Chrome usw.

ideawu
quelle
Dieses Bild beschreibt das Szenario sehr gut. Wäre toll gewesen, wenn ein Beispiel in Aktion gegeben worden wäre. Was passiert zum Beispiel, wenn eine Person eine Chatbox mit einem Freund öffnet (initiiert)? Wie kann sich Facebook auf dieses spezielle Gespräch einstellen und Nachrichten an beide Enden senden? (nur eine Vermutung: Ich kann mir nur vorstellen, dass das Anwendungsprogramm einen Socket öffnet und beide Client-Adressen bindet und dann einfach weiter zuhört und schreibt, wenn eine Nachricht in die Box geschrieben wird)
edam
5

Facebook verwendet MQTT anstelle von HTTP. Push ist besser als Polling. Über HTTP müssen wir den Server kontinuierlich abfragen, aber über den MQTT-Server wird die Nachricht an die Clients weitergeleitet.

Vergleich zwischen MQTT und HTTP: http://www.youtube.com/watch?v=-KNPXPmx88E

Hinweis: Meine Antworten passen am besten für mobile Geräte.

abhi
quelle
3
Darüber hinaus verwendet Google den GCM-Dienst für Android, der von Entwicklern zur Implementierung des Push-Nachrichtendienstes verwendet werden kann. developer.android.com/google/gcm/index.html Bitte akzeptieren Sie, wenn Sie die Antwort nützlich finden.
Abhi
5

Ein wichtiges Problem bei langen Abfragen ist die Fehlerbehandlung. Es gibt zwei Arten von Fehlern:

  1. Die Anforderung kann eine Zeitüberschreitung aufweisen. In diesem Fall sollte der Client die Verbindung sofort wiederherstellen. Dies ist ein normales Ereignis bei langen Abfragen, wenn keine Nachrichten eingegangen sind.

  2. Ein Netzwerkfehler oder ein Ausführungsfehler. Dies ist ein tatsächlicher Fehler, den der Client ordnungsgemäß akzeptieren und warten sollte, bis der Server wieder online ist.

Das Hauptproblem ist, dass die Clients den Server unter DOS ausführen würden, wenn Ihr Fehlerbehandler die Verbindung auch bei einem Fehler vom Typ 2 sofort wieder herstellt.

Beide Antworten mit Codebeispiel verpassen dies.

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
Ronenz
quelle