Vom Server gesendete Ereignisse und PHP - Was löst Ereignisse auf dem Server aus?

91

Alles,

HTML5 Rocks hat ein nettes Anfänger-Tutorial zu Server-Sended Events (SSE):

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

Ich verstehe jedoch kein wichtiges Konzept - was löst das Ereignis auf dem Server aus, durch das eine Nachricht gesendet wird?

Mit anderen Worten - im HTML5-Beispiel - sendet der Server einfach einmal einen Zeitstempel :

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

Wenn ich ein praktisches Beispiel erstellen würde - z. B. eine "Pinnwand" im Facebook-Stil oder einen Börsenticker, in dem der Server jedes Mal, wenn sich Daten ändern, eine neue Nachricht an den Client "sendet", wie funktioniert das?

Mit anderen Worten ... Verfügt das PHP-Skript über eine Schleife, die kontinuierlich ausgeführt wird, nach einer Änderung der Daten sucht und dann jedes Mal eine Nachricht sendet, wenn es eine findet? Wenn ja - woher wissen Sie, wann Sie diesen Prozess beenden müssen?

Oder - sendet das PHP-Skript einfach die Nachricht und endet dann (wie es im HTML5Rocks-Beispiel der Fall zu sein scheint)? Wenn ja - wie erhalten Sie fortlaufende Updates? Fragt der Browser die PHP-Seite einfach in regelmäßigen Abständen ab? Wenn ja - wie ist das ein "vom Server gesendetes Ereignis"? Wie unterscheidet sich dies vom Schreiben einer setInterval-Funktion in JavaScript, die AJAX verwendet, um eine PHP-Seite in regelmäßigen Abständen aufzurufen?

Entschuldigung - das ist wahrscheinlich eine unglaublich naive Frage. Aber keines der Beispiele, die ich finden konnte, macht dies deutlich.

[AKTUALISIEREN]

Ich denke, meine Frage war schlecht formuliert, daher hier einige Erläuterungen.

Angenommen, ich habe eine Webseite, auf der der aktuelle Kurs der Apple-Aktie angezeigt werden soll.

Wenn der Benutzer die Seite zum ersten Mal öffnet, erstellt die Seite eine EventSource mit der URL meines "Streams".

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

Meine Frage ist: Wie soll "stream.php" funktionieren?

So was? (Pseudocode):

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

Mit anderen Worten - bleibt "stream.php" geöffnet, solange der Client mit ihm "verbunden" ist?

Wenn ja - bedeutet dies, dass so viele Threads ausgeführt werden, stream.phpwie Sie gleichzeitig Benutzer haben? Wenn ja - ist dies aus der Ferne möglich oder eine geeignete Methode zum Erstellen einer Anwendung? Und woher wissen Sie, wann Sie eine Instanz von beenden können stream.php?

Mein naiver Eindruck ist, dass PHP in diesem Fall keine geeignete Technologie für diese Art von Server ist. Aber alle Demos, die ich bisher gesehen habe, implizieren, dass PHP dafür in Ordnung ist, weshalb ich so verwirrt bin ...

mattstuehler
quelle
Das ist der Teil, den ein Entwickler selbst codieren muss. Die Daten werden über Websockets / Long Polling usw. abgerufen. Der Trick ist jedoch, was das Ereignis auslöst. Persönlich habe ich mit einigen Ansätzen experimentiert und ein Ansatz, den ich mochte (der aber nicht so ausfallsicher war), bestand darin, MySQL jedes Mal ein Konsolenprogramm auslösen zu lassen, wenn etwas in eine bestimmte Tabelle eingefügt wurde. Das Konsolenprogramm würde die Informationen über geänderte / eingefügte Datensätze erhalten und eine Benachrichtigung über WebSockets an den entsprechenden Benutzer senden. Grundsätzlich hatte ich einen PHP-Daemon, der darauf wartete, Nachrichten zu versenden.
NB
Ein Problem dabei ist, dass SSE vom IE nicht unterstützt wird: - / Außerdem würde ich dieses Produkt lesen. Prodigyproductionsllc.com/articles/programming/javascript/… Ich denke, er verwendet einen Port, um das Problem mit zu vielen Kindern zu vermeiden, aber insgesamt sieht es so aus Empfehlung ist, SSE zu vermeiden. Sieht nach viel mehr Ärger aus, als es wert ist, IMO.
PJ Brunet
Derzeit nicht von IE11 oder Android Browser unterstützt caniuse.com/eventsource
PJ Brunet
1
Wenn jemand sse PHP-Code benötigt: github.com/shahzadthathal/server-sent-events-php-example
Muhammad Shahzad
4
Ich hatte die gleiche Frage und ich glaube, ich verstehe zutiefst, was Sie unter dem verstehen, was das Ereignis auf dem Server auslöst . Wenn Sie ein Objekt von erstellen EventSource('stream.php'), öffnet der Client eine Verbindung, mit stream.phpder er wie von Ajax aufgerufen wird. DIESE Verbindung löst Ihren serverseitigen Code aus und hält die Verbindung offen, solange Ihr serverseitiger Code etwas zu sagen hat. Dann wird die Verbindung geschlossen und nach einer kurzen Verzögerung (3 Sekunden in Chrome, glaube ich) öffnet der Client die Verbindung wieder, wodurch Ihre stream.phpDatei erneut ausgelöst wird.
Ahmad Maleki

Antworten:

28

"... bleibt" stream.php "geöffnet, solange der Client damit" verbunden "ist?"

Ja, und Ihr Pseudocode ist ein vernünftiger Ansatz.

"Und woher weißt du, wann du eine Instanz von stream.php BEENDEN kannst?"

Im typischsten Fall geschieht dies, wenn der Benutzer Ihre Site verlässt. (Apache erkennt den geschlossenen Socket und beendet die PHP-Instanz.) Die Hauptzeit, zu der Sie den Socket möglicherweise vom Server aus schließen, ist, wenn Sie wissen, dass für eine Weile keine Daten vorhanden sein werden. Die letzte Nachricht, die Sie dem Kunden senden, besteht darin, ihn aufzufordern, zu einem bestimmten Zeitpunkt zurückzukehren. In Ihrem Stock-Streaming-Fall können Sie beispielsweise die Verbindung um 20:00 Uhr schließen und Kunden anweisen, innerhalb von 8 Stunden zurückzukehren (vorausgesetzt, NASDAQ ist für Angebote von 4:00 bis 20:00 Uhr geöffnet). Freitagabend sagst du ihnen, sie sollen Montagmorgen zurückkommen. (Ich habe ein bevorstehendes Buch über SSE und widme ein paar Abschnitte zu diesem Thema.)

"... wenn dies der Fall ist, ist PHP keine geeignete Technologie für diese Art von Server. Aber alle Demos, die ich bisher gesehen habe, implizieren, dass PHP dafür in Ordnung ist, weshalb ich es auch bin verwirrt..."

Nun, die Leute argumentieren, dass PHP keine geeignete Technologie für normale Websites ist, und sie haben Recht: Sie könnten dies mit weitaus weniger Speicher- und CPU-Zyklen tun, wenn Sie Ihren gesamten LAMP-Stack durch C ++ ersetzen würden. Trotzdem versorgt PHP die meisten Websites mit Strom. Es ist eine sehr produktive Sprache für die Webarbeit, aufgrund einer Kombination aus einer vertrauten C-ähnlichen Syntax und so vielen Bibliotheken, und eine beruhigende Sprache für Manager, da viele PHP-Programmierer eingestellt werden müssen, viele Bücher und andere Ressourcen und einige große Anwendungsfälle (zB Facebook und Wikipedia). Dies sind im Grunde die gleichen Gründe, warum Sie PHP als Streaming-Technologie wählen könnten.

Das typische Setup ist nicht eine Verbindung zu NASDAQ pro PHP-Instanz. Stattdessen haben Sie einen anderen Prozess mit einer einzelnen Verbindung zum NASDAQ oder möglicherweise einer einzelnen Verbindung von jedem Computer in Ihrem Cluster zum NASDAQ. Dadurch werden die Preise entweder in einen SQL / NoSQL-Server oder in den gemeinsam genutzten Speicher verschoben. Dann fragt PHP nur den gemeinsam genutzten Speicher (oder die Datenbank) ab und überträgt die Daten. Oder haben Sie einen Datenerfassungsserver, und jede PHP-Instanz öffnet eine Socket-Verbindung zu diesem Server. Der Datenerfassungsserver sendet Aktualisierungen an jeden seiner PHP-Clients, sobald er sie empfängt, und sendet diese Daten wiederum an seinen Client.

Das Hauptproblem bei der Skalierbarkeit bei der Verwendung von Apache + PHP für das Streaming ist der Speicher für jeden Apache-Prozess. Wenn Sie das Speicherlimit der Hardware erreicht haben, treffen Sie die geschäftliche Entscheidung, dem Cluster einen weiteren Computer hinzuzufügen, oder schneiden Sie Apache aus der Schleife heraus und schreiben Sie einen dedizierten HTTP-Server. Letzteres kann in PHP erfolgen, sodass Ihr gesamtes vorhandenes Wissen und Ihr Code wiederverwendet werden können, oder Sie können die gesamte Anwendung in einer anderen Sprache umschreiben. Der reine Entwickler in mir würde einen dedizierten, optimierten HTTP-Server in C ++ schreiben. Der Manager in mir würde eine weitere Box hinzufügen.

Darren Cook
quelle
Ist es besser, stattdessen Nginx zu verwenden, da jeder Apache-Verbindungsprozess Speicher belegt?
Zhang Buzz
@ ZhangBuzz Wo ich Apache + PHP gesagt habe, bedeutet es wirklich "Webserver + PHP-Prozess", also im Grunde kein Unterschied zur Verwendung eines anderen Webservers.
Darren Cook
Vielleicht Server wie diese? github.com/hoaproject/Eventsource oder github.com/hhxsv5/php-sse
Enrique
Beachten Sie auch, dass Nginx dafür viel effizienter sein könnte, da weniger Speicher benötigt wird: blog.webfaction.com/2008/12/…
Enrique
31

Vom Server gesendete Ereignisse dienen zur Echtzeitaktualisierung von der Serverseite auf die Clientseite. Im ersten Beispiel wird die Verbindung vom Server nicht beibehalten, und der Client versucht alle 3 Sekunden erneut, eine Verbindung herzustellen, sodass vom Server gesendete Ereignisse keinen Unterschied zum Ajax-Polling machen.

Damit die Verbindung bestehen bleibt, müssen Sie Ihren Code in eine Schleife einschließen und ständig nach Updates suchen.

PHP ist threadbasiert und mehr verbundene Benutzer führen dazu, dass dem Server die Ressourcen ausgehen. Dies kann gelöst werden, indem die Skriptausführungszeit gesteuert und das Skript beendet wird, wenn eine Zeitspanne (dh 10 Minuten) überschritten wird. Die EventSourceAPI stellt automatisch wieder eine Verbindung her, sodass die Verzögerung in einem akzeptablen Bereich liegt.

Überprüfen Sie auch meine PHP-Bibliothek auf vom Server gesendete Ereignisse . Sie können mehr darüber erfahren, wie vom Server gesendete Ereignisse in PHP ausgeführt werden, und das Codieren vereinfachen.

Licson
quelle
5
Könnten Sie näher auf "Dies kann durch Steuern der Skriptausführungszeit und Beenden des Skripts, wenn es eine Zeitspanne überschreitet" eingehen? Wenn Sie eine übermäßige Anzahl von Benutzern haben, würde dies die Ressourcennutzung wirklich erheblich verbessern, indem die Verbindung geschlossen wird, da der Benutzer in 3 Sekunden nur erneut eine Verbindung herstellen würde?
Luke
4

Ich habe festgestellt, dass der sse techink alle paar Verzögerungsdaten an den Client sendet (so etwas wie das Umkehren des Pooling-Daten-Techinks von der Client-Seite ex Ajax-Pooling-Daten). Um dieses Problem zu lösen, habe ich dies auf einer sseServer.php-Seite gemacht:

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

und die sse.php ist:

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

Beachten Sie, dass ich in der Datei sseSerer.php eine Sitzung starte und eine Sitzungsvariable verwende! das Problem zu überwinden.

Außerdem rufe ich die sseServer.php variable messagejedes Mal über Ajax auf (Posten und Wert setzen auf ), wenn ich die Nachricht "aktualisieren" möchte.

Jetzt mache ich in der jQuery (Javascript) so etwas: 1.) Ich deklariere eine globale Variable var timeStamp = 0; 2.) Ich benutze den nächsten Algorithmus:

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

In der Zeile: $.notify("Please refresh "+event.data, "info"); gibt es, dass Sie die Nachricht verarbeiten können.

Für meinen Fall habe ich eine jQuery-Benachrichtigung gesendet.

Sie können stattdessen POSIX PIPES oder eine DB-Tabelle verwenden, um die "Nachricht" über POST zu übergeben, da die Datei sseServer.php so etwas wie eine "Endlosschleife" ausführt.

Mein Problem zu der Zeit ist, dass der obige Code die "Nachricht" nicht an alle Clients sendet, sondern nur an das Paar (der Client, der die sseServer.php aufgerufen hat, funktioniert für jedes Paar individuell), also ändere ich die Technik und auf a DB-Update von der Seite, auf der ich die "Nachricht" und dann die Datei sseServer.php auslösen möchte, um die Nachricht per POST zu erhalten, wird sie aus der DB-Tabelle abgerufen.

Ich hoffe ich habe Hilfe!

c.chasapis
quelle
3

Dies ist wirklich eine strukturelle Frage zu Ihrer Anwendung. Echtzeitereignisse sind etwas, über das Sie von Anfang an nachdenken möchten, damit Sie Ihre Anwendung darauf aufbauen können. Wenn Sie eine Anwendung geschrieben haben, die nur eine Reihe von zufälligen mysql(i)_queryMethoden mit Zeichenfolgenabfragen ausführt und diese nicht über einen Vermittler weiterleitet, haben Sie oft keine andere Wahl, als entweder einen Großteil Ihrer Anwendung neu zu schreiben oder dies zu tun ständige serverseitige Abfrage.

Wenn Sie Ihre Entitäten jedoch als Objekte verwalten und sie einer Zwischenklasse übergeben, können Sie sich in diesen Prozess einbinden. Schauen Sie sich dieses Beispiel an:

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

Wenn Sie in Ihrer Anwendung zum Speichern bereit sind:

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

Dies ist nicht das anmutigste Beispiel, aber es sollte als anständiger Baustein dienen. Sie können sich in Ihre eigentliche Persistenzschicht einbinden, um das Auslösen dieser Ereignisse zu handhaben. Dann erhalten Sie sie sofort (so in Echtzeit wie möglich), ohne Ihren Server zu belasten (da Sie Ihre Datenbank nicht ständig abfragen müssen, um festzustellen, ob sich etwas geändert hat).

Auf diese Weise werden Sie offensichtlich keine manuellen Änderungen an der Datenbank abfangen. Wenn Sie jedoch mit einer beliebigen Häufigkeit manuell Änderungen an Ihrer Datenbank vornehmen, sollten Sie entweder:

  • Beheben Sie das Problem, bei dem Sie eine manuelle Änderung vornehmen müssen
  • Erstellen Sie ein Tool, um den Prozess zu beschleunigen, und lösen Sie diese Ereignisse aus
Colin M.
quelle
3
Colin - danke für deine Antwort. Meine Schuld - meine Frage ist nicht klar - aber das ist nicht wirklich das, was ich frage. Was ich meinte zu fragen, ist dies ... Wenn Sie PHP als „Server“ verwenden - macht das PHP - Skript Sie aus dem Eventsource in der Client - Bedarf rufen Sie die laufen ganze Zeit der Client verbunden ist, um es? Würde das bedeuten, dass bei 1.000 gleichzeitigen Benutzern 1.000 separate Threads 1.000 gleichzeitige Instanzen Ihres PHP-Skripts ausführen? Ist das machbar? Und wie würden Sie wissen, wann Sie das PHP-Skript beenden müssen (vorausgesetzt, es ist eine Schleife, um "am Leben" zu bleiben)?
Mattstuehler
-7

Grundsätzlich ist PHP für solche Dinge nicht geeignet. Ja, Sie können es zum Laufen bringen, aber es wird eine Katastrophe bei hoher Last sein. Wir betreiben Stockserver, die Aktienänderungssignale über Websockets an Dutzende Tausend Benutzer senden - und wenn wir dafür PHP verwenden würden ... Nun, wir könnten, aber diese hausgemachten Zyklen - ist nur ein Albtraum. Jede einzelne Verbindung führt einen separaten Prozess auf dem Server durch, oder Sie müssen Verbindungen aus einer Datenbank verarbeiten.

Verwenden Sie einfach nodejs und socket.io. Damit können Sie in wenigen Tagen problemlos starten und einen laufenden Server haben. Nodejs hat auch eigene Einschränkungen, aber für Websockets (und SSE) -Verbindungen ist es jetzt die leistungsstärkste Technologie.

Und auch - SSE ist nicht so gut, wie es scheint. Der einzige Vorteil von Websockets ist, dass Pakete nativ komprimiert werden (ws wird nicht komprimiert), aber der Nachteil ist, dass SSE eine einseitige Verbindung ist. Wenn Ihr Benutzer dem Abonnement ein weiteres Aktiensymbol hinzufügen möchte, muss er eine Ajax-Anfrage stellen (einschließlich aller Probleme mit der Ursprungskontrolle, und die Anfrage ist langsam). In Websockets kommunizieren Client und Server in einer einzigen geöffneten Verbindung in beide Richtungen. Wenn der Benutzer also ein Handelssignal sendet oder ein Angebot abonniert, sendet er einfach eine Zeichenfolge in einer bereits geöffneten Verbindung. Und es ist schnell.

Prosto Trader
quelle
2
Sie können React.php grundsätzlich genauso verwenden wie die Ereignisschleife in node.js.
Matěj Koubík
3
Obwohl es gut zu sagen ist, dass PHP nicht die beste Wahl ist, denke ich, dass Sie zumindest etwas einschließen sollten, nach dem OP gefragt hat.
Nishchal Gautam