In che modo Facebook, Gmail inviano la notifica in tempo reale?


269

Ho letto alcuni post su questo argomento e le risposte sono cometa, reverse ajax, streaming http, push del server, ecc.

Come funziona la notifica della posta in arrivo su Gmail?

In che modo GMail Chat è in grado di effettuare richieste AJAX senza l'interazione con il cliente?

Vorrei sapere se ci sono riferimenti al codice che posso seguire per scrivere un esempio molto semplice. Molti post o siti web parlano solo della tecnologia. È difficile trovare un codice di esempio completo. Inoltre, sembra che molti metodi possano essere utilizzati per implementare la cometa, ad esempio Hidden IFrame, XMLHttpRequest. Secondo me, usare XMLHttpRequest è una scelta migliore. Cosa ne pensi dei pro e dei contro di diversi metodi? Quale utilizza Gmail?

So che deve farlo sia sul lato server che sul lato client. Esiste un codice di esempio PHP e Javascript?

Risposte:


428

Il modo in cui Facebook lo fa è piuttosto interessante.

Un metodo comune per eseguire tali notifiche è eseguire il polling di uno script sul server (utilizzando AJAX) in un determinato intervallo (forse ogni pochi secondi), per verificare se è successo qualcosa. Tuttavia, questo può richiedere molta rete e spesso fai richieste inutili, perché non è successo nulla.

Il modo in cui lo fa Facebook sta usando l'approccio della cometa, piuttosto che sondare un intervallo, non appena un sondaggio viene completato, ne emette un altro. Tuttavia, ogni richiesta allo script sul server ha un timeout estremamente lungo e il server risponde alla richiesta solo dopo che si è verificato qualcosa. Puoi vederlo accadere se apri la scheda Console di Firebug su Facebook, con richieste di uno script che potrebbero richiedere minuti. È davvero geniale, poiché questo metodo riduce immediatamente sia il numero di richieste sia la frequenza con cui devi inviarle. Ora disponi effettivamente di un framework di eventi che consente al server di "generare" eventi.

Dietro questo, in termini di contenuto effettivo restituito da tali sondaggi, c'è una risposta JSON, con quello che sembra essere un elenco di eventi e informazioni su di essi. È minimizzato, quindi è un po 'difficile da leggere.

In termini di tecnologia attuale, AJAX è la strada da percorrere qui, perché puoi controllare i timeout delle richieste e molte altre cose. Consiglierei (cliché di overflow dello stack qui) usando jQuery per eseguire AJAX, eliminerà molti problemi di compatibilità incrociata. In termini di PHP, potresti semplicemente eseguire il polling di una tabella del database del registro eventi nello script PHP e tornare al client solo quando succede qualcosa? Ci sono, mi aspetto, molti modi per implementarlo.

Implementazione:

Lato server:

Sembra che ci siano alcune implementazioni delle librerie di comete in PHP, ma a dire il vero, è davvero molto semplice, qualcosa come il seguente pseudocodice:

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

echo json_encode(get_events());
  • La funzione has_event_happened verificherebbe semplicemente se fosse successo qualcosa in una tabella degli eventi o qualcosa del genere, e quindi la funzione get_events restituirebbe un elenco delle nuove righe nella tabella? Dipende davvero dal contesto del problema.

  • Non dimenticare di modificare il tempo massimo di esecuzione di PHP, altrimenti si interromperà presto!

Dalla parte del cliente:

Dai un'occhiata al plugin jQuery per eseguire l'interazione Comet:

Detto questo, il plugin sembra aggiungere un bel po 'di complessità, è davvero molto semplice sul client, forse (con jQuery) qualcosa del tipo:

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

Il tutto dipende molto da come viene assemblata la tua architettura esistente.


2
È una spiegazione molto bella e dettagliata. Grazie. Hai qualche codice di esempio per uno dei tanti modi per implementarlo?
Billy,

45
Penso che etichettare PHP come un linguaggio / piattaforma che non si ridimensiona bene non sia necessariamente vero. Può essere utilizzato per sviluppare sistemi su larga scala. Guarda Facebook Se lo sviluppatore fa bene, allora si ridimensionerà, in caso contrario, non lo farà. L'uso di una piattaforma web specifica non è una garanzia di scalabilità. Oh, e anche, la domanda ha chiesto PHP.
Alistair Evans,

5
@Kazar: "Facebook utilizza PHP" è un po 'fuorviante - l'ultima volta che ho sentito, hanno sviluppato HipHop allo scopo esplicito di convertire PHP in C ++, poiché PHP non funzionava abbastanza bene.
cHao,

14
@cHao: È un punto giusto, tuttavia questa risposta è stata scritta nel 2009, prima che Facebook iniziasse a utilizzare hiphop. All'epoca Facebook era ancora un sistema su larga scala che utilizzava php da solo.
Alistair Evans,

6
Quindi la tecnica è mantenere una connessione costantemente aperta che manterrà un server in uno stress costante. Una quantità tipica di connessioni simultanee per un web server medio è di circa 200, ma il numero di utenti di Facebook che sono online contemporaneamente è molto più grande. Come lo fanno?
Paul,

43

Aggiornare

Mentre continuo a ricevere voti su questo, penso che sia ragionevole ricordare che questa risposta ha 4 anni. Il Web è cresciuto molto rapidamente, quindi tieni presente questa risposta.


Ho avuto lo stesso problema di recente e ho studiato l'argomento.

La soluzione fornita si chiama polling lungo e per utilizzarla correttamente è necessario assicurarsi che la richiesta AJAX abbia un timeout "grande" e di effettuare sempre questa richiesta dopo la fine corrente (timeout, errore o esito positivo).

Polling lungo - Client

Qui, per mantenere il codice breve, userò 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);

}

È importante ricordare che (dai documenti jQuery ):

In jQuery 1.4.x e precedenti, l'oggetto XMLHttpRequest sarà in uno stato non valido se la richiesta scade; l'accesso a qualsiasi membro dell'oggetto può generare un'eccezione. Solo in Firefox 3.0+, le richieste di script e JSONP non possono essere annullate da un timeout; lo script verrà eseguito anche se arriva dopo il periodo di timeout.

Polling lungo - Server

Non è in una lingua specifica, ma sarebbe qualcosa del genere:

function handleRequest () {  

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

     return events();

} 

Qui, hasTimedOutci assicureremo che il tuo codice non aspetti per sempre, e anythingHappenedcontrollerà se qualche evento è successo. Lo scopo sleepè di rilasciare il tuo thread per fare altre cose mentre non succede nulla. Il eventsrestituirà un dizionario di eventi (o di qualsiasi altra struttura dati si può preferire) in formato JSON (o qualsiasi altro si preferisce).

Risolve sicuramente il problema, ma, se sei preoccupato per la scalabilità e le prestazioni come ero durante la ricerca, potresti prendere in considerazione un'altra soluzione che ho trovato.

Soluzione

Usa le prese!

Sul lato client, per evitare problemi di compatibilità, utilizzare socket.io . Cerca di utilizzare direttamente il socket e presenta fallback su altre soluzioni quando i socket non sono disponibili.

Sul lato server, creare un server utilizzando NodeJS (esempio qui ). Il client si abbonerà a questo canale (osservatore) creato con il server. Ogni volta che deve essere inviata una notifica, questa viene pubblicata in questo canale e il sottoscrittore (client) viene avvisato.

Se non ti piace questa soluzione, prova APE ( Ajax Push Engine ).

Spero di averti aiutato.


pensi che 1 sia un sostituto dell'altro o che ci sia bisogno di entrambe le tecnologie nello stesso progetto?
TQ

Se intendi APE e NodeJS, puoi sceglierne uno. se intendi richieste AJAX periodiche e quella che ho suggerito, la mia soluzione potrebbe ricadere su quella ajax quando manca il supporto socket (consultare i documenti socket.io). In entrambi i casi, è necessaria solo una soluzione.
Walter Macambira,

Ehi Walter, vorrei usare il tuo suggerimento su uno dei miei siti. Sai dove posso trovare un server Sockets? Grazie!
Progo,

1
Puoi implementarlo. Node lo rende davvero semplice.
Walter Macambira,

Come rilevare hasTimedOut()?
Mobasher Fasihy,

18

Secondo una presentazione sul sistema di messaggistica di Facebook Facebook, Facebook utilizza la tecnologia delle comete per "inviare" messaggi ai browser web. Il server delle comete di Facebook è basato sul server Web Erlang di provenienza aperta mochiweb.

Nell'immagine seguente, la frase "cluster di canali" significa "server di comete".

Panoramica del sistema

Molti altri grandi siti web costruiscono il proprio server di comete, perché ci sono differenze tra le esigenze di ogni azienda. Ma costruire il proprio server di comete su un server di comete open source è un buon approccio.

Puoi provare icomet , un server cometa C ++ C1000K creato con libevent. icomet fornisce anche una libreria JavaScript, è facile da usare come:

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 supporta una vasta gamma di browser e sistemi operativi, tra cui Safari (iOS, Mac), IE (Windows), Firefox, Chrome, ecc.


Questa immagine descrive molto bene lo scenario. Sarebbe stato fantastico se fosse stato fornito un esempio in azione. Ad esempio, cosa succede quando una persona apre (avvia) una chat con un amico? In che modo Facebook si sintonizza su questa specifica conversazione e spinge i messaggi verso entrambi? (solo una supposizione: posso solo immaginare che il programma applicativo apra un socket e associ entrambi gli indirizzi client e poi continui ad ascoltare e scrivere ogni volta che viene scritto un messaggio nella casella)
edam

5

Facebook utilizza MQTT anziché HTTP. Push è meglio del polling. Tramite HTTP è necessario eseguire il polling continuo del server, ma tramite il server MQTT il messaggio viene inviato ai client.

Confronto tra MQTT e HTTP: http://www.youtube.com/watch?v=-KNPXPmx88E

Nota: le mie risposte si adattano meglio ai dispositivi mobili.


3
Inoltre, google utilizza il servizio GCM per Android, può essere utilizzato dagli sviluppatori per implementare il servizio di messaggi push. developer.android.com/google/gcm/index.html Ti preghiamo di accettare se trovi utile la risposta.
abhi,

5

Un problema importante con il polling lungo è la gestione degli errori. Esistono due tipi di errori:

  1. La richiesta potrebbe timeout, nel qual caso il client dovrebbe ristabilire immediatamente la connessione. Questo è un evento normale nel polling lungo quando non sono arrivati ​​messaggi.

  2. Un errore di rete o un errore di esecuzione. Questo è un errore reale che il client dovrebbe accettare con garbo e attendere che il server torni online.

Il problema principale è che se il gestore degli errori ristabilisce immediatamente la connessione anche per un errore di tipo 2, i client dovrebbero DOS il server.

Entrambe le risposte con esempio di codice mancano questo.

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.