web-dev-qa-db-fra.com

Comment facebook, gmail envoie-t-il la notification en temps réel?

J'ai lu quelques articles sur ce sujet et les réponses sont comet, ajax inversé, streaming http, serveur Push, etc.

Comment fonctionne la notification de courrier entrant sur Gmail?

Comment GMail Chat peut-il effectuer des requêtes AJAX sans interaction client?

J'aimerais savoir s'il existe des références de code que je peux suivre pour écrire un exemple très simple. De nombreux posts ou sites Web ne parlent que de technologie. Il est difficile de trouver un exemple de code complet. En outre, il semble que de nombreuses méthodes peuvent être utilisées pour implémenter la comète, par exemple. IFrame caché, XMLHttpRequest. À mon avis, utiliser 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?

261
Billy

La façon dont Facebook fait cela 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) afin de vérifier si quelque chose s'est passé. Cependant, cela peut demander beaucoup de ressources réseau et vous faites souvent des requêtes inutiles, car rien ne s’est passé.

La façon dont Facebook utilise l'approche de la comète, plutôt que d'interroger sur un intervalle, dès qu'un sondage est terminé, il en émet un autre. Toutefois, chaque requête adressée au script sur le serveur a un délai d’expiration extrêmement long et le serveur ne répond à la requête qu’une fois que quelque chose s’est passé. Vous pouvez voir cela se produire si vous ouvrez l'onglet Console de Firebug lorsque vous êtes sur Facebook, avec des requêtes à un script prenant éventuellement 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'un cadre d'événements permettant au serveur de "déclencher" des événements.

Derrière cela, en termes de contenu réel renvoyé par ces sondages, il s'agit d'une réponse JSON, avec ce qui semble être une liste d'événements et des informations les concernant. Cependant, il est un peu difficile à lire.

En ce qui concerne la technologie actuelle, AJAX est la voie à suivre ici, car vous pouvez contrôler les délais d'attente des demandes et bien d'autres choses. Je recommanderais (le cliché de débordement de pile ici) d'utiliser jQuery pour effectuer l'AJAX, cela éliminera de nombreux 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 produit? Je pense qu’il existe de nombreuses façons de mettre en œuvre cette solution.

Mise en oeuvre:

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 qui ressemble peut-être au pseudocode suivant:

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

echo json_encode(get_events());
  • La fonction has_event_happened vérifiera simplement si quelque chose s'est passé dans une table d'événements ou quelque chose du genre, puis la fonction get_events renverra 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 le délai d'attente expirera plus tôt!

Côté client:

Jetez un coup d'œil au plugin jQuery pour faire de l'interaction Comet:

Cela dit, le plugin semble ajouter un peu de complexité, il est vraiment très simple pour 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
});

Tout dépend de la manière dont votre architecture existante est construite.

421
Kazar

Mise à 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. Web a grandi à un rythme très rapide, soyez donc conscient de cette réponse.


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

La solution donnée est appelée interrogation longue, et pour l'utiliser correctement, vous devez être sûr que votre demande AJAX a un délai "important" et toujours effectuer cette demande après la fin du délai en cours (délai, erreur ou succès). .

Long Polling - Client

Ici, pour garder le code court, je vais utiliser 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 (de docs jQuery ):

Dans jQuery 1.4.x et inférieur, l'objet XMLHttpRequest sera dans un état non valide si la demande expire; l'accès à n'importe quel objet object peut générer une exception. Dans Firefox 3.0+ uniquement, les requêtes de script et JSONP ne peuvent pas être annulées avant l'expiration du délai; le script s'exécutera même s'il arrive après le délai d'attente.

Long Polling - Server

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, hasTimedOut s'assurera que votre code n'attendra pas éternellement, et anythingHappened vérifiera s'il se produit un événement. La sleep sert à libérer votre fil pour faire d'autres choses pendant que rien ne se passe. La events renverra un dictionnaire d'événements (ou toute autre structure de données que vous préférez) au format JSON (ou selon vos préférences).

Cela résout sûrement le problème, mais si vous êtes préoccupé par l’évolutivité et les performances, comme 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 le socket directement et a recours à d'autres solutions lorsque les sockets ne sont pas disponibles.

Côté serveur, créez un serveur utilisant 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) en est averti.

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

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

39
Walter Macambira

Selon un diaporama sur le système de messagerie de Facebook , Facebook utilise la technologie de la comète pour "transmettre" un message aux navigateurs Web. Le serveur comète de Facebook repose sur le serveur Web mochiweb Erlang à source ouverte.

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

System overview

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

Vous pouvez essayer icomet , un serveur de comète C1000K C++ construit avec libevent. icomet fournit également une bibliothèque JavaScript, il 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 un large éventail de navigateurs et de systèmes d'exploitation, notamment Safari (iOS, Mac), IE (Windows), Firefox, Chrome, etc.

18
ideawu

Facebook utilise MQTT au lieu de HTTP. Push est mieux que polling. Par HTTP, nous devons interroger le serveur en continu, mais via 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.

5
abhinav

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

  1. La demande peut expirer, auquel cas le client doit rétablir la connexion immédiatement. Ceci est un événement normal dans une longue interrogation quand 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 devrait 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, y compris en cas d'erreur de type 2, les clients le feraient sous DOS.

Cela manque aux deux réponses avec un exemple de code.

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