Eventi inviati dal server e php: cosa attiva gli eventi sul server?


91

Tutti,

HTML5 Rocks ha un bel tutorial per principianti sugli eventi inviati dal server (SSE):

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

Ma non capisco un concetto importante: cosa attiva l'evento sul server che causa l'invio di un messaggio?

In altre parole, nell'esempio HTML5, il server invia semplicemente un timestamp una volta :

<?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()));

Se stavo costruendo un esempio pratico, ad esempio un "muro" in stile Facebook o un ticker di borsa, in cui il server "spinge" un nuovo messaggio al client ogni volta che qualche dato cambia, come funziona?

In altre parole ... Lo script PHP ha un ciclo che viene eseguito continuamente, controllando un cambiamento nei dati, quindi inviando un messaggio ogni volta che ne trova uno? In tal caso, come fai a sapere quando terminare il processo?

Oppure - lo script PHP invia semplicemente il messaggio, quindi termina (come sembra essere il caso nell'esempio HTML5Rocks)? In tal caso, come si ottengono aggiornamenti continui? Il browser esegue semplicemente il polling della pagina PHP a intervalli regolari? In tal caso, come è un "evento inviato dal server"? In che modo è diverso dallo scrivere una funzione setInterval in JavaScript che utilizza AJAX per chiamare una pagina PHP a intervalli regolari?

Spiacenti, questa è probabilmente una domanda incredibilmente ingenua. Ma nessuno degli esempi che sono riuscito a trovare lo rende chiaro.

[AGGIORNARE]

Penso che la mia domanda fosse formulata male, quindi ecco alcuni chiarimenti.

Diciamo che ho una pagina web che dovrebbe visualizzare il prezzo più recente delle azioni Apple.

Quando l'utente apre la pagina per la prima volta, la pagina crea un EventSource con l'URL del mio "flusso".

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

La mia domanda è questa: come dovrebbe funzionare "stream.php"?

Come questo? (pseudo-codice):

<?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
    }
?>

In altre parole, "stream.php" rimane aperto finché il client è "connesso" ad esso?

Se è così, significa che hai tanti thread in esecuzione stream.phpquanti sono gli utenti simultanei? In tal caso, è fattibile in remoto o è un modo appropriato per creare un'applicazione? E come fai a sapere quando puoi terminare un'istanza di stream.php?

La mia ingenua impressione è che, in questo caso, PHP non sia una tecnologia adatta per questo tipo di server. Ma tutte le demo che ho visto finora implicano che PHP va bene per questo, motivo per cui sono così confuso ...


Questa è la parte che uno sviluppatore deve codificare da solo. I mezzi per ottenere i dati sono tramite websocket / polling lungo ecc. Tuttavia il trucco è ciò che fa scattare l'evento. Personalmente, ho sperimentato alcuni approcci e un approccio che mi è piaciuto (ma non era così sicuro) era fare in modo che MySQL attivasse un programma console ogni volta che qualcosa veniva inserito in una tabella specifica. Il programma della console riceverà le informazioni sul record modificato / inserito e invierà una notifica all'utente corrispondente tramite WebSocket. Fondamentalmente avevo un demone PHP in attesa di inviare messaggi in giro.
NB

Un problema con questo, SSE non è supportato da IE: - / Inoltre vorrei leggere questo prodigyproductionsllc.com/articles/programming/javascript/… Penso che stia usando una porta per evitare il problema dei troppi bambini ma nel complesso sembra il suo la raccomandazione è di evitare l'ESS. Sembra che ci siano molti più problemi di quanto valga la pena, IMO.
PJ Brunet

Attualmente non supportato da IE11 o browser Android caniuse.com/eventsource
PJ Brunet

1
Se qualcuno ha bisogno del codice sse php: github.com/shahzadthathal/server-sent-events-php-example
Muhammad Shahzad

4
Ho avuto la stessa domanda e credo profondamente capire cosa si intende per ciò che fa scattare l'evento sul server ... . Quando crei un oggetto di EventSource('stream.php'), il client apre una connessione con la stream.phpquale è come chiamarlo con ajax. QUESTA connessione attiva il codice lato server e mantiene la connessione aperta fino a quando il codice lato server ha qualcosa da dire. Quindi la connessione si chiude e dopo un breve ritardo (3 secondi in Chrome credo) il client riapre la connessione che attiva stream.phpnuovamente il file.
Ahmad Maleki

Risposte:


28

"..." stream.php "rimane aperto fintanto che il client è" connesso "ad esso?"

Sì, e il tuo pseudo-codice è un approccio ragionevole.

"E come fai a sapere quando puoi terminare un'istanza di stream.php?"

Nel caso più tipico, ciò accade quando l'utente lascia il tuo sito. (Apache riconosce il socket chiuso e uccide l'istanza PHP.) Il momento principale in cui potresti chiudere il socket dal lato server è se sai che non ci saranno dati per un po '; l'ultimo messaggio che invii al cliente è di dirgli di tornare a una certa ora. Ad esempio, nel tuo caso di streaming di stock, potresti chiudere la connessione alle 20:00 e dire ai clienti di tornare tra 8 ore (supponendo che NASDAQ sia aperto per preventivi dalle 4:00 alle 20:00). Venerdì sera gli dici di tornare lunedì mattina. (Ho un prossimo libro su SSE e dedico un paio di sezioni a questo argomento.)

"... se questo è il caso, PHP non è una tecnologia adatta per questo tipo di server. Ma tutte le demo che ho visto finora implicano che PHP va bene per questo, motivo per cui sono così confuso..."

Ebbene, le persone sostengono che PHP non sia una tecnologia adatta per i normali siti web, e hanno ragione: potresti farlo con molta meno memoria e cicli di CPU se sostituissi l'intero stack LAMP con C ++. Tuttavia, nonostante ciò, PHP alimenta la maggior parte dei siti là fuori perfettamente. È un linguaggio molto produttivo per il lavoro sul web, grazie a una combinazione di una sintassi familiare simile al C e così tante librerie, e confortante per i manager in quanto molti programmatori PHP da assumere, molti libri e altre risorse, e alcune grandi casi d'uso (ad esempio Facebook e Wikipedia). Questi sono fondamentalmente gli stessi motivi per cui potresti scegliere PHP come tecnologia di streaming.

La configurazione tipica non sarà una connessione al NASDAQ per istanza PHP. Invece avrai un altro processo con una singola connessione al NASDAQ, o forse una singola connessione da ogni macchina nel tuo cluster al NASDAQ. Ciò quindi spinge i prezzi in un server SQL / NoSQL o nella memoria condivisa. Quindi PHP esegue il polling della memoria condivisa (o del database) e invia i dati. Oppure, disponi di un server di raccolta dati e ogni istanza PHP apre una connessione socket a quel server. Il server di raccolta dati invia gli aggiornamenti a ciascuno dei suoi client PHP, non appena li riceve, e questi a loro volta inviano i dati al loro client.

Il principale problema di scalabilità con l'utilizzo di Apache + PHP per lo streaming è la memoria per ogni processo Apache. Quando si raggiunge il limite di memoria dell'hardware, prendere la decisione aziendale di aggiungere un'altra macchina al cluster o di escludere Apache dal ciclo e scrivere un server HTTP dedicato. Quest'ultimo può essere fatto in PHP in modo che tutte le tue conoscenze e il codice esistenti possano essere riutilizzati, oppure puoi riscrivere l'intera applicazione in un'altra lingua. Il puro sviluppatore in me scriverebbe un server HTTP dedicato e semplificato in C ++. Il manager in me aggiungerebbe un'altra scatola.


Poiché ogni processo di connessione di Apache consuma memoria, sarà meglio usare invece Nginx?
Zhang Buzz

@ZhangBuzz Dove ho detto Apache + PHP significa veramente "server web + processo PHP", quindi praticamente nessuna differenza con l'utilizzo di un server web diverso.
Darren Cook


Nota anche che Nginx potrebbe essere molto più efficiente per questo perché usa meno memoria: blog.webfaction.com/2008/12/…
Enrique

31

Gli eventi inviati dal server sono per l'aggiornamento in tempo reale dal lato server al lato client. Nel primo esempio, la connessione dal server non viene mantenuta e il client tenta di connettersi di nuovo ogni 3 secondi e rende gli eventi inviati dal server nessuna differenza rispetto al polling ajax.

Quindi, per rendere persistente la connessione, è necessario avvolgere il codice in un ciclo e verificare costantemente la presenza di aggiornamenti.

PHP è basato su thread e più utenti connessi faranno esaurire le risorse del server. Questo può essere risolto controllando il tempo di esecuzione dello script e terminando lo script quando supera un certo periodo di tempo (cioè 10 minuti). L' EventSourceAPI si connetterà di nuovo automaticamente in modo che il ritardo sia in un intervallo accettabile.

Inoltre, controlla la mia libreria PHP per gli eventi inviati dal server , puoi capire di più su come eseguire eventi inviati dal server in PHP e semplificare il codice.


5
Potresti approfondire "Questo può essere risolto controllando il tempo di esecuzione dello script e terminare lo script quando supera un certo periodo di tempo"? Se hai un numero eccessivo di utenti, migliorerebbe davvero molto l'utilizzo delle risorse chiudendo la connessione poiché l'utente si connetterebbe di nuovo in 3 secondi?
Luca

4

Ho notato che il techink sse invia ogni paio di dati di ritardo al client (qualcosa come l'inversione del collegamento tecnico dei dati di pool dalla pagina client ex dati di pooling Ajax.) Quindi per superare questo problema l'ho fatto su una pagina sseServer.php:

<?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'] );
?>

e sse.php è:

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

Si noti che su sseSerer.php avvio una sessione e utilizzo una variabile di sessione! per superare il problema.

Inoltre chiamo sseServer.php tramite Ajax (inserendo e impostando il valore su variable message) ogni volta che voglio "aggiornare" il messaggio.

Ora in jQuery (javascript) faccio qualcosa del genere: 1 °) dichiaro una variabile globale var timeStamp = 0; 2 °) utilizzo il prossimo algoritmo:

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 */

Alla riga di: $.notify("Please refresh "+event.data, "info"); è lì che puoi gestire il messaggio.

Nel mio caso inviavo una notifica jQuery.

È possibile utilizzare POSIX PIPES o una tabella DB invece per passare il "messaggio" tramite POST poiché sseServer.php fa qualcosa come un "ciclo infinito".

Il mio problema al momento è che il codice sopra NON INVIA IL "messaggio" a tutti i client ma solo alla coppia (il client che ha chiamato sseServer.php funziona come individuo per ogni coppia) quindi cambierò la tecnica e ad una Aggiornamento DB dalla pagina che desidero attivare il "messaggio" e quindi sseServer.php invece per ottenere il messaggio tramite POST lo otterrà dalla tabella DB.

Spero di avere aiuto!


3

Questa è davvero una domanda strutturale sulla tua applicazione. Gli eventi in tempo reale sono qualcosa a cui vuoi pensare dall'inizio, quindi puoi progettare la tua applicazione attorno ad essi. Se hai scritto un'applicazione che esegue solo una serie di mysql(i)_querymetodi casuali utilizzando query di stringa e non le passa attraverso alcun tipo di intermediario, molte volte non avrai altra scelta che riscrivere gran parte della tua applicazione o farlo polling costante sul lato server.

Se, tuttavia, gestisci le tue entità come oggetti e le passi attraverso una sorta di classe intermedia, puoi agganciarti a quel processo. Guarda questo esempio:

<?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
    }
}

Nella tua applicazione, quando sei pronto per salvare:

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

Questo non è l'esempio più grazioso, ma dovrebbe fungere da elemento costitutivo decente. Puoi agganciarti al tuo attuale livello di persistenza per gestire l'attivazione di questi eventi. Quindi li ottieni immediatamente (il più in tempo reale possibile) senza martellare il tuo server (dal momento che non hai bisogno di interrogare costantemente il tuo database e vedere se le cose sono cambiate).

Ovviamente non catturerai le modifiche manuali al database in questo modo, ma se stai facendo qualcosa manualmente al tuo database con qualsiasi frequenza, dovresti:

  • Risolvi il problema che richiede di effettuare una modifica manuale
  • Crea uno strumento per accelerare il processo e attiva questi eventi

3
Colin - grazie per la tua risposta. Colpa mia - la mia domanda non è chiara - ma non è proprio quello che sto chiedendo. Quello che intendevo chiedere è questo ... Se stai usando PHP come "server" - fa lo script PHP si chiama dal EventSource nel client necessità di eseguire il tutto il tempo il client è connesso ad esso? Ciò significherebbe che, se hai 1.000 utenti simultanei, hai 1.000 thread separati che eseguono 1.000 istanze simultanee del tuo script PHP? È fattibile? E come sapresti quando terminare lo script php (supponendo che sia in loop per rimanere "vivo")?
mattstuehler

-7

Fondamentalmente, PHP non è una tecnologia adatta per questo genere di cose. Sì, puoi farlo funzionare, ma sarà un disastro con carichi elevati. Gestiamo server di stock che inviano segnali di cambio di borsa tramite websocket a dozzine di migliaia di utenti - e se usassimo php per questo ... Beh, potremmo, ma quei cicli fatti in casa - è solo un incubo. Ogni singola connessione creerà un processo separato sul server o dovrai gestire le connessioni da una sorta di database.

Usa semplicemente nodejs e socket.io. Ti consentirà di avviare facilmente e avere un server in esecuzione in un paio di giorni. Nodejs ha anche dei limiti, ma per le connessioni websocket (e SSE) ora è la tecnologia più potente.

E inoltre - SSE non è così buono come sembra. L'unico vantaggio dei websocket è che i pacchetti vengono compressi con gzip in modo nativo (ws non è gzippato), ma il lato negativo è che SSE è una connessione unilaterale. Il tuo utente, se vuole aggiungere un altro simbolo di borsa a subscripton, dovrà fare una richiesta ajax (inclusi tutti i problemi con il controllo dell'origine e la richiesta sarà lenta). In websocket client e server comunicano in entrambi i modi in una singola connessione aperta, quindi se l'utente invia un segnale di trading o si iscrive al preventivo, invia semplicemente una stringa in una connessione già aperta. Ed è veloce.


2
Puoi usare React.php fondamentalmente allo stesso modo del ciclo di eventi in node.js.
Matěj Koubík

3
Anche se è bene dire che PHP non è la scelta migliore, penso che dovresti almeno includere qualcosa che OP ha chiesto.
Nishchal Gautam
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.