Montag, 16. Februar 2009
peXi - Zwischenstand Prototyp
Versuchsaufbau
Das Szenario ist zunächst so trivial wie möglich, zeigt jedoch bereits an welchen Stellen Probleme auftreten werden, so dass man diese schon frühzeitig bei der Architekturüberlegung berücksichtigen kann.
Im Prinzip sieht das Szenario wie folgt aus:
- Sender schickt Message an Middleware Server
- Server nimmt Message entgegen und legt sie in eine Queue
- Engine holt Message aus Queue und schickt sie an Empfänger
- Empfänger nimmt Message entgegen und speichert sie im Filesystem
Dieser einfache Aufbau beschränkt sich also erst einmal auf die asynchrone Verarbeitung von Testnachrichten vom Sender über die Middleware bis zum Empfänger.
Die verwendeten Komponenten werden im Folgenden näher beschrieben:
Komponenten
Sender
Script, welches über den Browser aufgerufen werden kann und dann per Zend_Http_Client beliebig viele HTTP-Post Nachrichten an den Middleware Server sendet. Der Einfachheit halber wird lediglich der Parameter "message" mit dem Wert von uniqid() gesendet, also noch keine "sinnvolle" Nachricht.
Die Nachrichten werden stumpf rausgefeuert, ohne speziell auf die Antwort des Servers zu reagieren.
Server
Script, welches vom Sender angesprochen wird und die gepostete Nachricht entgegen nimmt. Der Nachrichteninhalt wird in einer Queue gespeichert, welche eine InnoDb Tabelle mit den Feldern "id", "message" und "status" ist. Bei erfolgreichem Speichern in die Queue wird ein HTTP Response Code 200 an den Sender zurückgeschickt, ansonsten ein Fehlercode.
Engine
Die Engine ist die Hauptkomponente der Middleware und besteht aus einem langlaufenden PHP-CLI Script (per "while (true) { ... }"), welches kontinuierlich die Queue abfragt, ob neue, unverarbeitete Nachrichten vorhanden sind.
Ist dies nicht der Fall, wartet das Script 5 Sekunden vor der nächsten Abfrage.
Sind neue Nachrichten vorhanden, wird eine Beliebige selektiert und wiederrum per Zend_Http_Client als HTTP-Post Nachricht mit den Parametern "id" und "message" an den Receiver gesendet. Je nach Response Code wird die Nachricht in der Queue als "verarbeitet", "fehlerhaft" oder "neu" (zum erneuten Verarbeiten) markiert.
Receiver
Script, welches von der Engine angesprochen wird und die gepostete Nachricht entgegen nimmt. Der Nachrichteninhalt wird in einer Textdatei im Filesystem mit dem Namen der "id" und Inhalt der "message" abgelegt. Sollte die Datei wider Erwarten bereits existieren wird ein Fehlercode zurückgesendet.
Analyse
Da ich (noch) auf einem Windows Rechner entwickle, sind Aussagen über Performance nur schwer zu treffen, zunächst ging es mir um den reibungslosen Ablauf des Prozesses.
Der lansamste Teil in diesem Versuchsaufbau ist das Versenden der Nachrichten per HTTP-Post, da für jede eingehende und ausgehende Nachricht der Middleware HTTP Verbindungen zu Scripten aufgebaut werden müssen.
Da die Engine ein langlaufender Prozess ist, kann es passieren, dass je nach Konfiguration und Anzahl der verarbeiteten Nachrichten das Memory Limit erreicht wird.
Um eine parallele Verarbeitung mit mehreren Prozessen zu simulieren, habe ich testweise bis zu 6 Instanzen des Engine Scripts gestartet wobei sofort das Problem von Race Conditions auftrat.
Schlussfolgerungen
Benutzung einer Unix Umgebung, um bessere Aussagen über die Performance treffen und Forking nutzen zu können.
Testen von anderen Methoden zur Nachrichtenübertragung, z.B. per "cUrl", persistente HTTP Verbindungen und Versenden von mehreren Nachrichten auf einmal.
Abarbeitung der Queue mit Verwendung von Transaktionen und Locking um Race Conditions zu vermeiden.
Parallele Verarbeitung der Messages durch Prozess Forking.
Weiteres Vorgehen
Als nächstes werde ich synchrone Nachrichten implementieren, das heißt, dass die Nachrichten nicht in der Queue gespeichert werden um von der Engine abgearbeitet zu werden, sondern dass das Server selbst die Verarbeitung auslöst, um die Antwort des Empfängers an den Sender zurückgeben zu können.
Dazu muss der Verarbeitungsteil, der bislang fest in der Engine implementiert ist ausgelagert werden, damit dieser direkt vom Server Script aufgerufen werden kann. Dies hat außerdem den Vorteil, dass eine Umstellung der Engine auf Forking einfacher zu implementieren ist, da dann jeder nebenläufige Prozess eine eigenständige Verarbeitung auslöst.
Falls jemand weitere Ideen zur Optimierung hat, bitte per Kommentar mitteilen
Lars Strojny - #1 - 16.02.2009 14:00 - (Antwort)
Ich würde mich erstmal nicht so sehr um die Performance kümmer (premature optimization usw.), sondern erstmal den Prototyp unter Unix hinbekommen. Also Objekte bauen, die Prozesse rausforken, IPC über SHM machen und dann das Zeug laufen lassen. Für andere Plattformen bzw. für die Verteilung über mehrere Maschinen kann man dann das Verfahren für IPC bzw. Forking ändern und alles ist gut.
Ralf Heitmann - #1.1 - 23.02.2009 23:51 - (Antwort)
So wie Lars es Empfohlen hat würde ich es auch angehen. Optimiert werden kann später immer noch.
Marc Jakubowski - #1.2 - 24.02.2009 10:34 - (Antwort)
Jepp, werde als mich als nächstes mit dem Ausbau der Funktionalität beschäftigen.
Synchrone/Asynchrone Verarbeitung funktioniert schonmal.
Nächster Schritt wäre die Empfängerermittlung.
Kay - #2 - 22.02.2010 08:43 - (Antwort)
vor 3 Jahren gab es ein Projekt das sehr ähnlich zu sein scheint. Der ESB der entwickelt wurde was blackbird, ein ESB auf Basis von PHP. Leider wird der nicht mehr weiter entwickelt, du findest aber vielleicht noch etwas doku im netz.
http://coding.derkeiler.com/Archive/PHP/php.general/2007-07/msg01335.html


