Es mag in Mode sein, aber gibt es einen praktischen Grund, Knoten zu übernehmen.js? Lee Boynton zeigt, wie es verwendet werden kann, um einen Echtzeit-News-Feed zu Ihrer PHP-Site hinzuzufügen.

Dieser Artikel soll Ihnen zeigen, wie Sie Node verwenden können, um Ihrer PHP-basierten Website Echtzeitfunktionen hinzuzufügen. Zuerst werden wir uns etwas genauer ansehen, was Node zu einer guten Lösung für Echtzeit-Apps macht, bevor wir zeigen, wie Sie einen Echtzeit-Newsfeed erstellen und in Ihre PHP-Website integrieren.

Threadbasiert vs. ereignisbasiert

Traditionell wird PHP mit Apache und dem Modul mod_php bedient. Wenn Sie den Befehl ‚top‘ auf Ihrem Unix-basierten Webserver ausführen, werden Sie wahrscheinlich eine große Anzahl von Apache-Prozessen sehen, die Webclients bedienen. In diesem Setup erzeugt jede Clientanforderung normalerweise einen neuen Apache-Prozess, bis der gesamte verfügbare RAM aufgebraucht ist. In letzter Zeit haben sich nginx und php-fpm als die effizienteste Methode zur Bereitstellung von PHP-Websites herausgestellt, aber selbst in diesem Setup wird jeder Client von einem anderen PHP-Prozess bedient. Der entscheidende Punkt hierbei ist, dass eine Clientanforderung von Anfang bis Ende einen PHP-Prozess für die gesamte Dauer verbraucht. Wenn die Bearbeitung jeder Anfrage lange dauert, können die Ressourcen des Servers sehr schnell aufgebraucht sein.

In Node bedient ein einzelner Knotenprozess typischerweise jeden Client in einer Ereignisschleife. Für langwierige, teure Prozesse wie den Zugriff auf das Dateisystem, eine Datenbank oder eine Remote-API wird empfohlen, asynchrone Methodenaufrufe anstelle von blockierenden zu verwenden. Dies wird durch die Verwendung von Callbacks erreicht, die ausgelöst werden, wenn eine Aktion wie der Zugriff auf das Dateisystem beendet ist. Dies bedeutet, dass ein einzelner Knotenprozess weiterhin neue Anforderungen verarbeiten kann, während der teure Vorgang im Hintergrund ausgeführt wird. Wenn die Ereignisoperation abgeschlossen ist, kehrt sie in die Ereignisschleifenwarteschlange zurück, um vom Knoten weiter verarbeitet zu werden.

Im Wesentlichen kann Node als eine ähnliche Umgebung zum Erstellen von Anwendungen wie Pythons Twisted oder EventMachine in Ruby angesehen werden. Node verfügt außerdem über einen integrierten produktionsbereiten HTTP-Server, sodass für die Ausführung kein separater Server wie Apache oder Nginx erforderlich ist, was den schlanken Ressourcenbedarf weiter erhöht (abgesehen von Speicherlecks).

var http = require('http');http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello Worldn');}).listen(1337, '127.0.0.1');console.log('Server running at http://127.0.0.1:1337/');

Das obige Codebeispiel zeigt, wie Sie einen obligatorischen „hello World“ -Webserver in nur wenigen Codezeilen schreiben können. Die erste Zeile zeigt die Verwendung des als CommonJS bekannten Modulsystems, mit dem Node separate Module einschließt. Die Funktion require ist integriert und importiert in diesem Fall das HTTP-Modul des Knotens zur Verwendung in der Anwendung. In der zweiten Zeile wird ein neues Webserverobjekt erstellt. Beachten Sie, dass der erste Parameter der createServer-Methode eine anonyme Funktion ist. Die meisten Methoden in Node akzeptieren eine Rückruffunktion als Parameter, und dies ist der Schlüssel zum Erstellen ereignisgesteuerter Anwendungen.

Die nächste Zeile der Ausführung ist Zeile 5, die die Methodenverkettung verwendet, um die Listen-Methode für den Rückgabewert der createServer-Methode aufzurufen (der zurückgegebene Wert ist die HTTP-Modulinstanz). Die Listen-Methode bewirkt, dass der Server HTTP-Anforderungen an Port 1337 auf localhost akzeptiert. Die letzte Zeile schreibt eine Nachricht an die Konsole, die besagt, dass der Server gestartet wurde. Nur wenn eine Anfrage an den Server gestellt wird, wird die anonyme Funktion aufgerufen, die den HTTP-Statuscode auf 200 OK setzt und den Content-Type Header setzt. Die ‚Hello World‘-Nachricht wird schließlich in den HTTP-Antworttext in Zeile 4 geschrieben.

Warum sollte ich Node verwenden.js dann?

Das ereignisgesteuerte Modell von Node eignet sich besonders für Echtzeitanwendungen wie Spiele, Newsfeeds und Chat-Anwendungen. Darüber hinaus können Sie im Frontend und Backend dieselbe Sprache verwenden. JavaScript wird immer beliebter, da immer mehr Rich-Client-seitige Anwendungen erstellt werden und Webbrowser JavaScript schneller ausführen können. Der Wechsel zwischen den Sprachen kann frustrierend sein.

WordPress Spezial

Entwickler Magazin Spezial Vol. 10: WordPressEntwickler Magazin Spezial Vol. 10: WordPress
Alles rund um WordPress im Entwickler Spezial Vol. 10 – ab 18.11.2016 im Handel.
WordPress ist länger als ein simples Blogsystem. In diesem Sonderheft beschäftigen sich zahlreiche Spezialisten mit allen wichtigen Fragen rund um das breiteste CMS im Web; und dabei geht es nicht nur um klassische Themen, sondern auch um Affiliate Marketing, E-Commerce u.v.m.

es hat gute Unterstützung für WebSockets. Es ist zwar möglich, WebSockets in PHP zu unterstützen, aber die asynchrone Natur von Node und der integrierte HTTP-Server passen besser zusammen. WebSockets sind eine Möglichkeit, eine dauerhafte Verbindung zum Browser aufrechtzuerhalten, um Daten schnell an den Client zu senden. Im Vergleich zu früheren Lösungen wie Long Polling oder Comet haben WebSockets eine viel geringere Latenz, da es keinen Overhead gibt, eine HTTP-Verbindung jedes Mal zu instanziieren, wenn einige Daten gesendet werden müssen. Der Nachteil von WebSockets ist, dass es sich um eine HTML5-Funktion handelt, die in Browsern nicht so gut unterstützt wird wie einfaches altes Ajax. Es ist jedoch möglich, in Browsern, die WebSockets nicht unterstützen, auf alternative Techniken wie lange Abfragen zurückzugreifen.

Auf der diesjährigen International PHP Conference erfahren Sie mehr über die Integration von Nodes.js mit PHP!

Die International PHP Conference ist die weltweit erste PHP-Konferenz und seit mehr als einem Jahrzehnt ein Meilenstein für erstklassige pragmatische Expertise in PHP und Webtechnologien . Auf der IPC treffen sich international renommierte Experten aus der PHP-Branche mit PHP-Anwendern und Entwicklern aus großen und kleinen Unternehmen. Schauen Sie sich unsere Frühbucherpreise für tolle Rabatte an!

Hier sind einige Highlight-Sessions von IPC:

  • Machen Sie Ihre Angular.js Application accessible
    Dirk Ginader (Google)
  • TypeScript – Die JavaScript-Entwicklung produktiver und robuster machen
    Rainer Stropek (software architects gmbh)
  • Wie man sich auf PHP 7 vorbereitet
    Sebastian BergmannthePHP.cc )

Beachten Sie jedoch, dass Node im Vergleich zu PHP eine unreife Plattform ist. Ursprünglich in 2009 erstellt, steckt es noch in den Kinderschuhen und hat noch nicht die Version 1.0 erreicht – wenn Ihnen das wichtig ist. Möglicherweise stellen Sie fest, dass sich die von Ihnen verwendeten APIs in Zukunft ändern oder dass Sie kein Framework finden, das dieselben Funktionen wie Ihr bevorzugtes PHP-Framework bietet. Nach meiner Erfahrung bestehen die verfügbaren Bibliotheken und Frameworks von Drittanbietern in der Regel aus viel kleineren Funktionsbündeln, die Sie zusammenfügen müssen.

Es besteht auch ein größeres Risiko, dass Speicherlecks Ihre Anwendung zum Stillstand bringen. Knotenprozesse werden normalerweise kontinuierlich ausgeführt, während PHP-Prozesse in der Regel regelmäßig neu erstellt werden, um die Auswirkungen von Speicherlecks zu negieren.

Der integrierende Teil

Der Newsfeed wird in eine einfache PHP-Website integriert, die Benutzeranmeldungen und -sitzungen verwaltet, wobei ein gemeinsames Setup von php-fpm und nginx verwendet wird. Wir werden JavaScript verwenden, um mit einer Knotenanwendung auf der Serverseite zu kommunizieren und den Newsfeed dynamisch zu aktualisieren, ohne die Seite neu zu laden. Zunächst jedoch eine kurze Zusammenfassung der Sitzungen.

Wenn Sie dies noch nicht tun, sollten Sie einen zentralen Speicherbereich für Ihre Sitzungen verwenden (Abbildung 1). Memcached kann einfach für diese Aufgabe verwendet werden, indem der integrierte Session Save-Handler in der PECL Memcached-Erweiterung verwendet wird. Wenn Sie den Server, auf dem Ihre Sitzungen gespeichert sind, neu starten möchten, ohne Daten zu verlieren, ist Redis eine gute Wahl. In jedem Fall können Sie mit einem zentralisierten Sitzungsspeicher Ihre Anwendung auf mehrere Webserver verteilen. Außerdem können Sie Sitzungsdaten für Anwendungen freigeben, die mit anderen Programmiersprachen erstellt wurden.

Abbildung 1: Architektur für gemeinsam genutzte Sitzungen.

Es gibt jedoch ein kleines Problem beim Parsen von Sitzungsdaten. Im Folgenden wird das Standard-Serialisierungsformat einer PHP-Sitzung angezeigt:

not|a:2:{i:0;s:4:"easy";i:1;a:1:{s:2:"to";s:5:"parse";}}

Es mag so aussehen, als könnten Sie die Zeichenfolgenmanipulation verwenden, um sie zu analysieren, aber es könnte Randfälle geben, die schwierig zu lösen sind. Es wäre schön, wenn die Sitzung im beliebten JSON-Format serialisiert würde:

{"this":{"is": "easier", "to": "parse"}}

Viel besser. Es ist ziemlich einfach, einen eigenen Sitzungsserialisierer zu schreiben, der alles, was Sie in $ _SESSION speichern, in das JSON-Format konvertiert, siehe meine Version.

Alternativ können Sie msgpack in Betracht ziehen, das für die Serialisierung von Sitzungen konfiguriert werden kann, wie im folgenden Code, der auch zeigt, wie memcached als Sitzungsspeicherhandler verwendet wird.


Es gibt eine Drittanbieter-Bibliothek für Node, die msgpack serialisieren und deserialisieren kann, verfügbar in npm (Node Package Manager, enthält eine große Anzahl von Modulen, die in Ihrer Anwendung mit nur einer einfachen npm-Installation verwendet werden können). Wir werden nun den Newsfeed mit Node erstellen und in eine PHP-Anwendung integrieren.

PHP App

Die PHP-Anwendung verarbeitet einfach Benutzeranmeldungen und Sitzungen. Sitzungen werden in memcached gespeichert, können jedoch problemlos in Redis gespeichert werden. Der folgende Code zeigt Ausschnitte der einfachen einseitigen Anwendung (die vollständige Quelle finden Sie unter github.com/lboynton/phphants-march-php ).

// // <!]>

Die erste Zeile des Skripts enthält die Composer-Autoloader-Datei, was bedeutet, dass alle Abhängigkeiten automatisch geladen werden, sodass keine separaten Include- oder Require-Zeilen erforderlich sind (Anmerkung der Redaktion: Weitere Informationen zu Composer finden Sie unter Jefersson Nathan de O. Chaves‘ Artikel). In diesem Fall ist der Session Save Handler der einzige externe Code, der benötigt wird. Es ist jedoch sehr einfach, Pakete von Drittanbietern, die auf packagist oder anderswo verfügbar sind, zu einem späteren Zeitpunkt mit Composer einzubinden, das ebenfalls automatisch zum automatischen Laden hinzugefügt wird. In den Zeilen 3 und 4 wird eine Verbindung zu memcached hergestellt, während der Session Save Handler in den Zeilen 5-14 initialisiert und bei PHP registriert wird. Benutzer werden einfach durch Angabe des Benutzernamens als GET-Parameter angemeldet, obwohl dies in einem realen System durch ein funktionaleres Authentifizierungssystem ersetzt werden sollte.

Auf der JavaScript-Seite verwenden wir einige Bibliotheken von Drittanbietern : jQuery, underscore.js und die Socket.IO client, der Browserteil des Knotenmoduls, mit dem wir Daten an den Browser senden. Das io.der Methodenaufruf connect erstellt eine Verbindung zur Knotenanwendung, die je nach Browserunterstützung einen von mehreren Transporten wie Websockets oder Ajax verwenden kann. Steckdose.die on-Methode richtet einen Ereignishandler ein, der eine Nachricht auf der Seite rendert, wenn ein ’news‘-Ereignis von der Serverseite ausgelöst wird.

Hinweis: Die Anwendung verwendet Composer zum Installieren des Session Save-Handlers und Bower, einen Paketmanager zum Installieren clientseitiger Assets. Um Bower zu installieren, führen Sie ’npm -g install bower‘ aus. Führen Sie dann ‚bower install‘ aus, um die Assets zu installieren. Alternativ können Sie die Skript-Tags und Stylesheets mit lokalen Versionen aktualisieren.

Die Anwendung wird mit nginx bereitgestellt. Hier ist die Nginx-Konfigurationsdatei:

upstream node { server localhost:3000;}server { listen 8080; server_name php-node-demo.localhost; root /home/lee/public_html/php-node-demo; index index.php; location / { try_files $uri $uri/ /index.php?$args; } location ~ .php$ { include fastcgi_params; fastcgi_index index.php; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } location ~ /socket.io { proxy_pass http://node; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }}

Die Zeilen 1-3 definieren einen Upstream-Server, auf dem die Knoten-App ausgeführt wird. Jede Anfrage endet in .php wird an php-fpm (Zeilen 15-20) gesendet, während alle Anfragen mit /socket .io in der URL wird an die Knoten-App übergeben (Zeilen 22-27). In den Zeilen 25 und 26 wird Nginx angewiesen, den Protokollwechsel zu unterstützen und die Weiterleitung von Websockets an den Knoten zu ermöglichen. Dies bedeutet, dass die PHP-App und die Knoten-App für den Client beide auf demselben Port ausgeführt werden.

Knoten.js App

Der Knoten.die App verarbeitet einfach alle Client-Anfragen für den Newsfeed. Snippets davon werden unten angezeigt (siehe github.com/lboynton/phphants-march-node für die vollständige Quelle).

io.set('authorization', function(handshake, callback) { var cookies = cookie.parse(handshake.headers.cookie); mcClient.get('sessions/' + cookies.PHPSESSID, function(error, result) { if (error) { callback(error, false); } else if (result) { handshake.session = JSON.parse(result); callback(null, true); } else { callback('Could not find session ID ' + cookies.PHPSESSID + ' in memcached', false); } });});io.sockets.on('connection', function(socket) { var session = socket.handshake.session; sockets = socket;);function getNews() { redis.blpop('news', 0, function(err, data) {news = JSON.parse(data);if (typeof news.to !== 'undefined') { if (typeof sockets !== 'undefined') {sockets.emit('news', news.content); }} else { io.sockets.emit('news', news.content);}process.nextTick(getNews); });}

Buchse.io ist eine Bibliothek, die eine einzige API für die WebSocket-Kommunikation zwischen Client und Server bereitstellt. Es unterstützt auch einen ordnungsgemäßen Fallback, wenn WebSockets im Browser nicht verfügbar sind, und andere nützliche Funktionen in einem Messaging-Protokoll wie Heartbeats, Timeouts und Trennungsunterstützung, die mit der HTML5-WebSocket-API nicht standardmäßig unterstützt werden.

Das erste Snippet konfiguriert die Autorisierung für Anforderungen, die Socket.io empfängt. Wenn eine neue Anforderung eingeht, werden die Cookies in der Anforderung analysiert und versucht, einen zwischengespeicherten Schlüssel abzurufen, der dem Wert des PHPSESSID-Cookies (dem Standardnamen für das PHP-Sitzungscookie) entspricht. Wenn gefunden, wird der analysierte Wert des Memcached-Schlüssels in Daten gespeichert.sitzung, auf die später zugegriffen werden kann.

Das nächste Snippet konfiguriert, was passieren soll, wenn Socket.io löst das Ereignis ‚Verbindung‘ aus. Dieses Ereignis wird während der ersten Verbindung vom Client ausgelöst, sobald es autorisiert wurde. Die Sitzungsdaten, die zuvor aus memcached abgerufen wurden, können nun über den Socket referenziert werden.handshake variabel. Wenn der Client eine Verbindung herstellt, wird die Socket-Instanz mit dem Benutzernamen des Clients verknüpft, sodass Nachrichten an einzelne Benutzer gesendet werden können.

Das letzte Snippet enthält eine Funktion, um nach neuen Nachrichten in einer Redis-Warteschlange zu suchen. Wenn die Warteschlange leer ist, wird sie blockiert, bis einige Daten an die Warteschlange angehängt werden, bevor sie geöffnet werden. Wenn einige Daten aus der Warteschlange entfernt werden können, werden sie als JSON analysiert, bevor festgelegt wird, ob der Inhalt an jeden verbundenen Client oder nur an einen einzelnen Benutzer gesendet werden soll. Der Nachrichteninhalt wird an den richtigen Socket gesendet, indem die Methode emit () für den Socket aufgerufen wird, der dem Benutzer zuvor im Verbindungsereignishandler zugeordnet war.

Schließlich der Prozess des Knotens.Die Methode nextTick() wird mit der Funktion getNews als Argument aufgerufen. Dies bedeutet, dass die Funktion getNews beim nächsten Ausführen der Ereignisschleife aufgerufen wird und die Redis-Warteschlange weiterhin auf Daten überprüft, bis die Anwendung gestoppt wird.

Verwenden Sie zur Installation npm, um die erforderlichen Abhängigkeiten herunterzuladen. Führen Sie dann die Anwendung mit node app aus.js.

Sie sollten nun in der Lage sein, einen Webbrowser zu öffnen und zu http://localhost:8080/?username=bob zu navigieren und die Newsfeed-Anwendung anzuzeigen. Öffnen Sie nun einen zweiten Browser-Tab oder ein Fenster mit einem anderen Benutzernamen, zum Beispiel http://localhost:8080/?username=sally.

Aktualisieren des Feeds

Beim Aktualisieren des Feeds wird einfach eine neue Nachricht in die Warteschlange gestellt. Stellen Sie mit dem Befehl redis-cli eine Verbindung zur Redis-CLI-Schnittstelle her. Dies ist eine interaktive Shell für Redis, mit der Sie Befehle direkt an den Server senden können. Das folgende Codebeispiel zeigt, wie Sie in die Warteschlange gelangen:

rpush news '{"content": "Testy test", "to": "bob"}'

In dem Webbrowser-Fenster, das Sie für den Benutzer bob geöffnet haben, sollte die Nachricht vom oberen Rand der Seite nach unten verschoben werden. Alternativ können Sie Nachrichten sowohl an bob, sally als auch an alle anderen verbundenen Clients senden, indem Sie den Parameter „to“ wie folgt ausschließen:

rpush news '{"content": "Everyone should see this"}'

In einer realen Anwendung würden Sie Daten von PHP aus in die Warteschlange schieben, z. B. mit der Erweiterung php-redis oder predis.

Fazit

Dies ist nur ein einfaches Beispiel, um zu demonstrieren, wie Node in Ihre PHP-Anwendung integriert werden kann. Es gibt einige Einschränkungen bei der Implementierung. Beispielsweise wird nur eine Verbindung von jedem Benutzer gespeichert. Wenn ein Benutzer mehrere Registerkarten oder Fenster für den Newsfeed geöffnet hat, wird daher nur eine Seite aktualisiert. Dies kann behoben werden, indem ein Array von Sockets pro Benutzer gespeichert und jede Verbindung und Trennung verfolgt wird, um die Sockets zum Array hinzuzufügen oder daraus zu entfernen. Es bleibt dem Leser überlassen, eine bessere Lösung zu implementieren. Es zeigte auch Tools wie Composer und Bower, die ich Ihnen dringend empfehle, in Ihren Anwendungen zu verwenden.Lee Boynton ist ein Entwickler mit Sitz in Hampshire, UK, mit einem besonderen Interesse an Echtzeit-Anwendungen wie Instant Messaging und Activity Streams. Er verfügt über Kenntnisse in der serverseitigen Entwicklung und Administration sowie in der Frontend-Entwicklung. Er arbeitet bei der lokalen Firma Symbios Group und ist auch Mitglied von PHP Hampshire, für die er bei der Organisation von Veranstaltungen hilft.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.