Comment Facebook et Gmail envoient-ils la notification en temps réel?

269

J'ai lu quelques articles sur ce sujet et les réponses sont comète, ajax inverse, streaming http, push serveur, etc.

Comment fonctionne la notification de courrier entrant sur Gmail?

Comment GMail Chat est-il capable de faire des requêtes AJAX sans interaction avec le client?

Je voudrais savoir s'il existe des références de code que je peux suivre pour écrire un exemple très simple. De nombreux articles ou sites Web ne font que parler de la technologie. Il est difficile de trouver un exemple de code complet. De plus, il semble que de nombreuses méthodes peuvent être utilisées pour implémenter la comète, par exemple Hidden IFrame, XMLHttpRequest. À mon avis, l'utilisation de XMLHttpRequest est un meilleur choix. Que pensez-vous des avantages et des inconvénients des différentes méthodes? Lequel utilise Gmail?

Je sais qu'il doit le faire à la fois côté serveur et côté client. Existe-t-il un exemple de code PHP et Javascript?

Gamelle
la source

Réponses:

428

La façon dont Facebook le fait est assez intéressante.

Une méthode courante pour effectuer de telles notifications consiste à interroger un script sur le serveur (à l'aide d'AJAX) à un intervalle donné (peut-être toutes les quelques secondes), pour vérifier si quelque chose s'est produit. Cependant, cela peut être assez intensif en réseau, et vous faites souvent des demandes inutiles, car rien ne s'est produit.

La façon dont Facebook le fait utilise l'approche comète, plutôt que d'interroger sur un intervalle, dès qu'un sondage est terminé, il en émet un autre. Cependant, chaque demande au script sur le serveur a un délai d'expiration extrêmement long, et le serveur ne répond à la demande qu'une fois que quelque chose s'est produit. Vous pouvez voir cela se produire si vous affichez l'onglet Console de Firebug sur Facebook, avec des demandes de script pouvant prendre quelques minutes. C'est vraiment très ingénieux, car cette méthode réduit immédiatement le nombre de demandes et la fréquence à laquelle vous devez les envoyer. Vous disposez désormais d'une infrastructure d'événements qui permet au serveur de «déclencher» des événements.

Derrière cela, en termes de contenu réel retourné par ces sondages, c'est une réponse JSON, avec ce qui semble être une liste d'événements et des informations à leur sujet. Il est cependant minifié, donc c'est un peu difficile à lire.

En termes de technologie réelle, AJAX est la voie à suivre ici, car vous pouvez contrôler les délais d'expiration des demandes et bien d'autres choses. Je recommanderais (cliché de débordement de pile ici) d'utiliser jQuery pour faire l'AJAX, cela éliminera beaucoup de problèmes de compatibilité croisée. En termes de PHP, vous pouvez simplement interroger une table de base de données du journal des événements dans votre script PHP et ne retourner au client que lorsque quelque chose se passe? Il y a, je pense, de nombreuses façons de mettre cela en œuvre.

Exécution:

Du côté serveur:

Il semble y avoir quelques implémentations de bibliothèques de comètes en PHP, mais pour être honnête, c'est vraiment très simple, quelque chose comme le pseudocode suivant:

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

echo json_encode(get_events());
  • La fonction has_event_happened vérifierait simplement si quelque chose s'est passé dans une table d'événements ou quelque chose, puis la fonction get_events retournerait une liste des nouvelles lignes de la table? Cela dépend vraiment du contexte du problème.

  • N'oubliez pas de modifier votre temps d'exécution PHP max, sinon il expirera tôt!

Côté client:

Jetez un oeil au plugin jQuery pour faire l'interaction Comet:

Cela dit, le plugin semble ajouter un peu de complexité, c'est vraiment très simple sur le client, peut-être (avec jQuery) quelque chose comme:

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

Le tout dépend beaucoup de la façon dont votre architecture existante est constituée.

Alistair Evans
la source
2
C'est une explication très agréable et détaillée. Je vous remercie. Avez-vous un exemple de code pour l'une des nombreuses façons de l'implémenter?
Billy
45
Je pense que l'étiquetage de PHP en tant que langage / plate-forme qui ne s'adapte pas bien n'est pas nécessairement vrai. Il peut être utilisé pour développer des systèmes à très grande échelle. Regardez facebook. Si le développeur le fait correctement, il évoluera, sinon, il ne le sera pas. L'utilisation d'une plateforme Web spécifique n'est pas une garantie d'évolutivité. Oh, et aussi, la question demandait PHP.
Alistair Evans
5
@Kazar: "Facebook utilise PHP" est un peu trompeur - pour la dernière fois, ils ont développé HipHop dans le but exprès de convertir PHP en C ++, car PHP ne fonctionnait pas assez bien.
cHao
14
@cHao: C'est un bon point, mais cette réponse a été écrite en 2009, avant que facebook ne commence à utiliser hiphop. À l'époque, Facebook était encore un système à très grande échelle utilisant php seul.
Alistair Evans,
6
La technique consiste donc à garder une connexion constamment ouverte, ce qui maintiendra un serveur dans une tension constante. Une quantité typique de connexions simultanées pour un serveur Web moyen est d'environ 200, mais le nombre d'utilisateurs de Facebook qui sont en ligne simultanément est beaucoup plus important. Comment font-ils cela?
Paul
43

Mettre à jour

Alors que je continue à recevoir des votes positifs à ce sujet, je pense qu'il est raisonnable de se rappeler que cette réponse a 4 ans. Le Web s'est développé à un rythme très rapide, alors soyez attentif à cette réponse.


J'ai eu le même problème récemment et fait des recherches sur le sujet.

La solution proposée est appelée longue interrogation, et pour l'utiliser correctement, vous devez être sûr que votre requête AJAX a un "grand" délai d'attente et toujours faire cette demande après la fin actuelle (délai d'attente, erreur ou succès).

Interrogation longue - Client

Ici, pour garder le code court, j'utiliserai 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);

}

Il est important de se rappeler que (à partir des documents jQuery ):

Dans jQuery 1.4.x et versions antérieures, l'objet XMLHttpRequest sera dans un état non valide si la demande expire; l'accès à n'importe quel membre d'objet peut lever une exception. Dans Firefox 3.0+ uniquement, les requêtes de script et JSONP ne peuvent pas être annulées par un délai d'expiration; le script s'exécutera même s'il arrive après la période d'expiration.

Interrogation longue - Serveur

Ce n'est pas dans une langue spécifique, mais ce serait quelque chose comme ceci:

function handleRequest () {  

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

     return events();

} 

Ici, hasTimedOuts'assurera que votre code n'attend pas éternellement et anythingHappenedvérifiera si un événement se produit. C'est sleeppour libérer votre thread pour faire d'autres choses alors que rien ne se passe. Le eventsrenvoie un dictionnaire d'événements (ou toute autre structure de données que vous préférez) au format JSON (ou tout autre que vous préférez).

Cela résout sûrement le problème, mais, si vous êtes préoccupé par l'évolutivité et la performance comme je l'étais lors de mes recherches, vous pourriez envisager une autre solution que j'ai trouvée.

Solution

Utilisez des prises!

Côté client, pour éviter tout problème de compatibilité, utilisez socket.io . Il essaie d'utiliser directement le socket et a des solutions de rechange à d'autres solutions lorsque les sockets ne sont pas disponibles.

Côté serveur, créez un serveur à l'aide de NodeJS (exemple ici ). Le client s'abonnera à ce canal (observateur) créé avec le serveur. Chaque fois qu'une notification doit être envoyée, elle est publiée sur ce canal et le souscripteur (client) est notifié.

Si vous n'aimez pas cette solution, essayez APE ( Ajax Push Engine ).

J'espère que j'ai aidé.

Walter Macambira
la source
pensez-vous que 1 est un remplacement pour l'autre ou est-il nécessaire pour les deux technologies sur le même projet?
tq
Si vous voulez dire APE et NodeJS, vous pouvez en choisir un. si vous voulez dire des demandes AJAX périodiques et celle que j'ai suggérée, ma solution peut revenir à celle ajax quand il manque le support de socket (référez-vous aux documents socket.io). Dans les deux cas, vous n'avez besoin que d'une seule solution.
Walter Macambira
Salut Walter, je voudrais utiliser votre suggestion sur l'un de mes sites. Savez-vous où je peux obtenir un serveur Sockets? Merci!
Progo
1
Vous pouvez l'implémenter. Node le rend vraiment simple.
Walter Macambira
Comment détecter hasTimedOut()?
Mobasher Fasihy
18

Selon un diaporama sur le système de messagerie de Facebook, Facebook utilise la technologie des comètes pour «pousser» les messages vers les navigateurs Web. Le serveur de comètes de Facebook est construit sur le serveur Web Erlang open source mochiweb.

Dans l'image ci-dessous, l'expression "groupes de canaux" signifie "serveurs comètes".

Présentation du système

De nombreux autres grands sites Web construisent leur propre serveur de comètes, car il existe des différences entre les besoins de chaque entreprise. Mais construire votre propre serveur de comète sur un serveur de comète open source est une bonne approche.

Vous pouvez essayer icomet , un serveur de comètes C1000K C ++ construit avec libevent. icomet fournit également une bibliothèque JavaScript, elle est facile à utiliser aussi simple que:

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 prend en charge une large gamme de navigateurs et de systèmes d'exploitation, y compris Safari (iOS, Mac), IE (Windows), Firefox, Chrome, etc.

ideawu
la source
Cette image décrit très bien le scénario. Cela aurait été formidable si un exemple en action avait été donné. Par exemple, que se passe-t-il lorsqu'une personne ouvre (initie) une boîte de discussion avec un ami? Comment Facebook se connecte à cette conversation spécifique et pousse les messages à la fois? (juste une supposition: je ne peux qu'imaginer que le programme d'application ouvre un socket et lie les deux adresses client, puis continue à écouter et à écrire chaque fois qu'un message est écrit dans la boîte)
edam
5

Facebook utilise MQTT au lieu de HTTP. Pousser vaut mieux que d'interroger. Via HTTP, nous devons interroger le serveur en continu, mais via le serveur MQTT, le message est envoyé aux clients.

Comparaison entre MQTT et HTTP: http://www.youtube.com/watch?v=-KNPXPmx88E

Remarque: mes réponses conviennent le mieux aux appareils mobiles.

abhi
la source
3
De plus, Google utilise le service GCM pour Android, il peut être utilisé par les développeurs pour implémenter le service de message push. developer.android.com/google/gcm/index.html Veuillez accepter si vous trouvez la réponse utile.
abhi
5

Un problème important avec l'interrogation longue est la gestion des erreurs. Il existe deux types d'erreurs:

  1. La demande peut expirer, auquel cas le client doit rétablir immédiatement la connexion. Il s'agit d'un événement normal lors d'une longue interrogation lorsque aucun message n'est arrivé.

  2. Une erreur de réseau ou une erreur d'exécution. Il s'agit d'une erreur réelle que le client doit accepter gracieusement et attendre que le serveur revienne en ligne.

Le problème principal est que si votre gestionnaire d'erreurs rétablit immédiatement la connexion également pour une erreur de type 2, les clients DOS le serveur.

Les deux réponses avec l'exemple de code manquent cela.

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
la source