110 votes

Événements envoyés par le serveur et php - qu'est-ce qui déclenche les événements sur le serveur ?

Tous,

HTML5 Rocks propose un bon tutoriel pour débutants sur les événements envoyés par le serveur (SSE) :

http://www.html5rocks.com/en/tutorials/eventsource/basics/

Mais je ne comprends pas un concept important : qu'est-ce qui déclenche l'événement sur le serveur qui provoque l'envoi d'un message ?

En d'autres termes - dans l'exemple HTML5 - le serveur envoie simplement un horodatage une fois :

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));

Si je construisais un exemple pratique - par exemple, un "mur" de type Facebook ou une vignette boursière, dans lequel le serveur "pousserait" un nouveau message vers le client chaque fois qu'un élément de données change, comment cela fonctionne-t-il ?

En d'autres termes... Est-ce que le script de PHP a une boucle qui tourne continuellement, vérifiant un changement dans les données, puis envoyant un message chaque fois qu'il en trouve un ? Si c'est le cas - comment savoir quand mettre fin à ce processus ?

Ou bien, le script PHP script envoie-t-il simplement le message, puis se termine (comme cela semble être le cas dans l'exemple de HTML5Rocks) ? Si c'est le cas, comment obtenez-vous des mises à jour continues ? Le navigateur interroge-t-il simplement la page PHP à intervalles réguliers ? Si oui, en quoi est-ce un "événement envoyé par le serveur" ? En quoi est-ce différent de l'écriture d'une fonction setInterval en JavaScript qui utilise AJAX pour appeler une page PHP à intervalles réguliers ?

Désolé - c'est probablement une question incroyablement naïve. Mais aucun des exemples que j'ai pu trouver n'est clair à ce sujet.

[MISE À JOUR]

Je pense que ma question était mal formulée, alors voici quelques éclaircissements.

Disons que j'ai une page Web qui doit afficher le cours le plus récent de l'action Apple.

Lorsque l'utilisateur ouvre la page pour la première fois, celle-ci crée un EventSource avec l'URL de mon "stream".

var source = new EventSource('stream.php');

Ma question est la suivante : comment doit fonctionner "stream.php" ?

Comme ça ? (pseudo-code) :

<?php
    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
    function sendMsg($msg) {
        echo "data: $msg" . PHP_EOL;
        echo PHP_EOL;
        flush();
    }

    while (some condition) {
        // check whether Apple's stock price has changed
        // e.g., by querying a database, or calling a web service
        // if it HAS changed, sendMsg with new price to client
        // otherwise, do nothing (until next loop)
        sleep (n) // wait n seconds until checking again
    }
?>

En d'autres termes, le fichier "stream.php" reste-t-il ouvert tant que le client y est "connecté" ?

Si oui, cela signifie-t-il que vous avez autant de fils en cours d'exécution stream.php comme vous avez des utilisateurs simultanés ? Si oui, est-ce faisable à distance, ou est-ce une façon appropriée de construire une application ? Et comment savez-vous quand vous pouvez FIN une instance de stream.php ?

Mon impression naïve est que, si c'est le cas, PHP n'est pas une technologie appropriée pour ce type de serveur. Mais toutes les démonstrations que j'ai vues jusqu'à présent laissent entendre que PHP convient parfaitement à ce type de serveur, d'où ma confusion...

35voto

Licson Points 1125

Les événements envoyés par le serveur servent à la mise à jour en temps réel du côté serveur vers le côté client. Dans le premier exemple, la connexion du serveur n'est pas maintenue et le client essaie de se reconnecter toutes les 3 secondes, ce qui fait que les événements envoyés par le serveur ne font aucune différence avec le polling ajax.

Ainsi, pour que la connexion persiste, vous devez envelopper votre code dans une boucle et vérifier constamment les mises à jour.

PHP est basé sur les threads et plus d'utilisateurs connectés feront que le serveur manquera de ressources. Ce problème peut être résolu en contrôlant le temps d'exécution du script et en mettant fin au script lorsqu'il dépasse une certaine durée (par exemple 10mins). Le site EventSource L'API se reconnectera automatiquement afin que le délai soit dans une fourchette acceptable.

Consultez également mon Bibliothèque PHP pour les événements envoyés par le serveur vous pourrez mieux comprendre comment créer des événements envoyés par le serveur en PHP et faciliter le codage.

34voto

Darren Cook Points 5743

"...est-ce que "stream.php" reste ouvert aussi longtemps que le client est "connecté" à ce site ?"

Oui, et votre pseudo-code est une approche raisonnable.

"Et comment savez-vous quand vous pouvez END une instance de stream.php ?"

Dans le cas le plus typique, cela se produit lorsque l'utilisateur quitte votre site. (Apache reconnaît la fermeture du socket et tue l'instance PHP). La principale raison pour laquelle vous pouvez fermer le socket du côté serveur est que vous savez qu'il n'y aura pas de données pendant un certain temps ; le dernier message que vous envoyez au client est de lui dire de revenir à une certaine heure. Par exemple, dans le cas de votre flux d'actions, vous pouvez fermer la connexion à 20 heures et dire aux clients de revenir dans 8 heures (en supposant que le NASDAQ est ouvert aux cotations de 4 heures à 20 heures). Le vendredi soir, vous leur dites de revenir le lundi matin. (J'ai un livre à paraître sur l'ESS, et je consacre quelques sections à ce sujet).

"...si c'est le cas, PHP n'est pas une technologie adaptée à ce genre de serveur. Mais toutes les démonstrations que j'ai vues jusqu'à présent impliquent que PHP est parfait pour cela. est parfait pour cela, c'est pourquoi je suis si confus...".

Les gens affirment que PHP n'est pas une technologie adaptée aux sites web normaux, et ils ont raison : vous pourriez le faire avec beaucoup moins de mémoire et de cycles CPU si vous remplaciez toute votre pile LAMP par C++. Cependant, malgré cela, PHP alimente très bien la plupart des sites existants. Il s'agit d'un langage très productif pour le travail sur le web, en raison de la combinaison d'une syntaxe familière de type C et de nombreuses bibliothèques, et d'un langage réconfortant pour les gestionnaires car il y a beaucoup de programmeurs PHP à embaucher, beaucoup de livres et d'autres ressources, et quelques cas d'utilisation importants (par exemple Facebook et Wikipedia). Ce sont essentiellement les mêmes raisons pour lesquelles vous pourriez choisir PHP comme technologie de streaming.

La configuration typique n'est pas une connexion au NASDAQ par instance PHP. Au lieu de cela, vous allez avoir un autre processus avec une seule connexion au NASDAQ, ou peut-être une seule connexion de chaque machine de votre cluster au NASDAQ. Cela pousse ensuite les prix soit dans un serveur SQL/NoSQL, soit dans une mémoire partagée. Ensuite, PHP interroge la mémoire partagée (ou la base de données), et envoie les données. Ou bien, vous avez un serveur de collecte de données, et chaque instance de PHP ouvre une connexion socket à ce serveur. Le serveur de collecte de données envoie des mises à jour à chacun de ses clients PHP, au fur et à mesure qu'il les reçoit, et ils envoient à leur tour ces données à leur client.

Le principal problème d'évolutivité lié à l'utilisation d'Apache+PHP pour le streaming est la mémoire de chaque processus Apache. Lorsque vous atteignez la limite de mémoire du matériel, vous devez prendre la décision d'ajouter une autre machine au cluster, ou de couper Apache de la boucle et d'écrire un serveur HTTP dédié. Ce dernier peut être réalisé en PHP, de sorte que toutes vos connaissances et votre code existants peuvent être réutilisés, ou vous pouvez réécrire l'ensemble de l'application dans un autre langage. Le développeur pur en moi écrirait un serveur HTTP dédié et simplifié en C++. Le gestionnaire en moi ajouterait une autre boîte.

4voto

c.chasapis Points 11

J'ai remarqué que le techink sse envoie des données au client tous les deux jours (quelque chose comme l'inversion des données de pooling techink de la page du client, par exemple les données de pooling Ajax :

<?php
        session_start();
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache'); // recommended to prevent caching of event data
        require 'sse.php';
        if ($_POST['message'] != ""){
                $_SESSION['message'] = $_POST['message'];
                $_SESSION['serverTime'] = time();
        }
        sendMsg($_SESSION['serverTime'], $_SESSION['message'] );
?>

et le sse.php est :

<?php
function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
?>

Remarquez que dans le fichier sseSerer.php, je démarre une session et utilise une variable de session pour résoudre le problème.

J'appelle également le sseServer.php via Ajax (en affichant et en définissant la valeur à variable message ) chaque fois que je veux "mettre à jour" le message.

Maintenant, dans le jQuery (javascript) je fais quelque chose comme ça : 1) je déclare une variable globale var timeStamp=0 ; 2) J'utilise l'algorithme suivant :

if(typeof(EventSource)!=="undefined"){
        var source=new EventSource("sseServer.php");
        source.onmessage=function(event)
        if ((timeStamp!=event.lastEventId) && (timeStamp!=0)){
                /* this is initialization */
                timeStamp=event.lastEventId;
                $.notify("Please refresh "+event.data, "info");
        } else {
                if (timeStamp==0){
                         timeStamp=event.lastEventId;
                }
        } /* fi */

} else {
        document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events...";
} /* fi */

A la ligne de : $.notify("Please refresh "+event.data, "info"); est là que vous pouvez traiter le message.

Dans mon cas, j'ai utilisé une notification jQuery.

Vous pouvez utiliser POSIX PIPES ou une table de base de données pour transmettre le "message" via POST, car le sseServer.php fait quelque chose comme une "boucle infinie".

Mon problème actuel est que le code ci-dessus n'envoie pas le "message" à tous les clients mais seulement à la paire (le client qui a appelé le sseServer.php fonctionne individuellement pour chaque paire). Je vais donc changer la technique et faire une mise à jour de la base de données à partir de la page qui doit déclencher le "message", puis le sseServer.php, au lieu de recevoir le message par POST, le recevra de la table de la base de données.

J'espère que j'ai de l'aide !

3voto

Colin Morelli Points 6539

Il s'agit en fait d'une question structurelle concernant votre application. Les événements en temps réel sont une chose à laquelle vous devez penser dès le début, afin de pouvoir concevoir votre application en conséquence. Si vous avez écrit une application qui se contente d'exécuter un tas d'événements aléatoires, vous pouvez vous attendre à ce qu'il y ait des événements en temps réel. mysql(i)_query à l'aide de requêtes de type chaîne et ne les transmet pas par un quelconque intermédiaire, vous n'aurez souvent d'autre choix que de réécrire une grande partie de votre application ou de procéder à une interrogation constante du serveur.

Toutefois, si vous gérez vos entités en tant qu'objets et que vous les faites passer par une sorte de classe intermédiaire, vous pouvez vous brancher sur ce processus. Regardez cet exemple :

<?php
class MyQueryManager {
    public function find($myObject, $objectId) {
        // Issue a select query against the database to get this object
    }

    public function save($myObject) {
        // Issue a query that saves the object to the database
        // Fire a new "save" event for the type of object passed to this method
    }

    public function delete($myObject) {
        // Fire a "delete" event for the type of object
    }
}

Dans votre application, lorsque vous êtes prêt à enregistrer :

<?php
$someObject = $queryManager->find("MyObjectName", 1);
$someObject->setDateTimeUpdated(time());
$queryManager->save($someObject);

Ce n'est pas l'exemple le plus gracieux, mais il devrait servir d'élément de base. Vous pouvez vous connecter à votre couche de persistance réelle pour gérer le déclenchement de ces événements. Vous les obtenez alors immédiatement (en temps réel) sans solliciter votre serveur (puisque vous n'avez pas besoin d'interroger constamment votre base de données pour voir si les choses ont changé).

Il est évident que vous ne pourrez pas détecter les modifications manuelles apportées à la base de données de cette manière, mais si vous effectuez des modifications manuelles de votre base de données à une fréquence quelconque, vous devriez le faire :

  • Corrigez le problème qui vous oblige à effectuer une modification manuelle.
  • Créez un outil pour accélérer le processus et déclenchez ces événements.

-7voto

Prosto Trader Points 3099

En fait, PHP n'est pas une technologie adaptée à ce genre de choses. Oui, vous pouvez le faire fonctionner, mais ce sera un désastre en cas de forte charge. Nous faisons tourner des serveurs d'actions qui envoient des signaux boursiers via des websockets à des dizaines de milliers d'utilisateurs - et si nous utilisions PHP pour cela... Eh bien, nous pourrions, mais ces cycles faits maison - est juste un cauchemar. Chaque connexion fait l'objet d'un processus distinct sur le serveur ou vous devez gérer les connexions à partir d'une sorte de base de données.

Utilisez simplement nodejs et socket.io. Cela vous permettra de démarrer facilement et d'avoir un serveur opérationnel en quelques jours. Nodejs a aussi ses propres limites, mais pour les connexions websockets (et SSE), c'est la technologie la plus puissante.

Et aussi - l'ESS n'est pas aussi bonne qu'elle le semble. Le seul avantage des websockets est que les paquets sont gzippés nativement (ws n'est pas gzippé), mais l'inconvénient est que SSE est une connexion unilatérale. L'utilisateur, s'il veut ajouter un autre symbole boursier à l'indice, devra faire une demande ajax (avec tous les problèmes de contrôle d'origine et la demande sera lente). Dans les websockets, le client et le serveur communiquent dans les deux sens dans une seule connexion ouverte. Ainsi, si l'utilisateur envoie un signal de négociation ou s'abonne à une cotation, il lui suffit d'envoyer une chaîne dans une connexion déjà ouverte. Et c'est rapide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X