Apache access.log mit node.js in Realtime parsen und zur Weiterverabeitung in RabbitMQ queuen

This entry is part 3 of 3 in the series node.js

Für ein firmeninternes Projekt (nein, hier werden keine ‘Internals’ preisgegeben ;) ) werte ich Apache access.log Dateien aus. Dies geschieht aktuell mittels eines Cronjobs, welcher ein PHP Script startet und das Log Zeile für Zeile ausliest, die Daten via Regular Expressions und StringSplit parsed und anschließend in eine MySQL-Datenbank schreibt. Das Problem hierbei ist, dass sowohl die Dateien (Logrotation ist auf 15 Minuten eingestellt) als auch die Zeilen seriell bearbeitet werden und dies natürlich sehr lange dauert. Zwischenzeitlich habe ich mir bereits gearman angeschaut, um trotz der Verwendung mit PHP diese Prozesse zu parallelisieren, jedoch habe ich hierbei bereits nach kurzer Zeit die Flinte ins Korn geworfen.

Nachdem ich mich nun mittlerweile seit einiger Zeit mit Node.js beschäftigt habe und nach einer knappen Woche auch gut mit RabbitMQ und dem Node.js-Modul node-amqp zurechtkomme, kam mir eine neue Idee. Warum nicht das access.log in Echtzeit auslesen, neu hinzugekommene Zeilen in eine RabbitMQ Queue zu schreiben und dann mit mehreren Worker-Prozessen parallel zu parsen und in eine Datenbank zu schreiben. Somit müsste nicht mehr auf die nächste Log-Rotation (15 min) und die Fertigstellung des Parsing-Scripts gewartet werden und es stünden die Daten nahezu in Echtzeit zur Verfügung.

Hier also die ersten Schritte, um das neue Konzept umzusetzen:

Schritt 1 – Vorbereitung

Zuerst laden wir die Module, welche für das Projekt benötigen:

Schritt 2 – vorbereiten und erstellen der Verbindung zu RabbitMQ

Da die Informationen aus dem access.log direkt in eine Queue geschrieben werden sollen, um sie später parallel von mehreren Workern verarbeiten zu können, muss zuerst eine Verbindung mit RabbitMQ hergestellt werden, welcher diese Queues verwaltet.

Den defaultExchangeName  habe ich bewusst ein eine gesonderte Variable geschrieben, da später noch die Queue an den Exchange gebunden werden muss und dort der Name erneut benötigt wird. Darüber hinaus wird lediglich die Verbindung mit den entsprechenden Benutzerdaten zu dem angegebenen Host aufgebaut.

Schritt 3 – Überprüfen der Verfügbarkeit und Erstellen der Queue

Wir warten auf das ‘ready’-Event der Verbindung zu RabbitMQ und legen anschließend die notwendigen Parameter für die von uns benötigte Queue fest, falls diese nicht vorhanden sein sollte und erstellt werden muss. Sollte diese nicht bereits vorhanden sein, wird sie mit den von uns vorgegebenen Werten erstellt, ansonsten wird die vorhandenen Queue genutzt, wie sie beim Erstellen konfiguriert wurde. Ist die Überprüfung bzw. die Erstellung abgeschlossen, emitten wir das Event ‘ready’, um mit dem Einlesen des Logs zu beginnen.

Schritt 4 – Einlesen des access.log in Echtzeit

Ich habe mir einige Module angeschaut, welche dabei helfen sollen, Dateien kontinuierlich einzulesen, welche von einem fremden Prozess permanent erweitert werden. Dazu gehörten u.a. fs.watchchokidar und carrier. Leider half mir keines bei meinem Problem. Ich habe mich daher entschlossen, auf das Tool ‘tail’ zurückzugreifen, welches mittels des Parameters -f kontinuierlich die letzten Zeilen des Logs auf der Standardausgabe ausgibt. Dieses Programm ist normalerweise nur unter Linux standardmäßig vorhanden, jedoch gibt es auch seit einiger Zeit eine entsprechende Portierung für Windows. Tail wird daher für unsere Zwecke in einem Child-Prozess gestartet und die Ausgabe als Buffer-Stream weiterverarbeitet.

Im ersten Schritt habe ich den Buffer in einen UTF-8 String umgewandelt und diesen über die Newline-Zeichen gesplittet, um für jede Zeile des Logs einen eigenen String zu erhalten. Bei einer Erweiterung des Scripts durch Worker-Prozesse, welche die Hauptaufgabe des Parsens übernehmen sollen, wäre zu überlegen, ob es nicht sinnvoller und schneller wäre, erst dort der Buffer zu splitten. Aktuell bleiben wir aber erstmal bei der vorhandenen Lösung.

Für das Durchlaufen der einzelnen Felder des Arrays verwende ich das ‘async’-Modul für Node.js, welches das asynchrone bearbeiten der Felder ermöglicht, so dass hier ein weiterer Performancegewinn zu verzeichnen ist. Dabei wird für jeden String überprüft, ob dieser leer ist und sollte dies nicht der Fall sein, wird dieser über rabbitMQ.publish und den entsprechenden defaultExchange an die Queue gesendet.

Schritt 5 – Überprüfen der Ergebnisse in RabbitMQ

Wenn wir uns die Konsole und die entsprechende Queue in RabbitMQ ansehen, können wir beobachten, wie die Zugriffe auf den Webserver in Echtzeit ausgelesen und gequeued werden:

 

 

Series Navigation<< Dynamisches Setzen von NODE_ENV für Produktions- bzw. Entwicklungsumgebung mit Node.js

You may also like...

  • http://www.eberhart-versicherungsmakler.de Robert

    Ich verfolge deine Seite schon seit längerem. Sind zwar viele Artikel die du schreibst, du könntest ab mal wieder nen Picdump einschieben. So zur auflockerung :)

    • http://www.feldstudie.net Torsten

      Hallo Robert,

      freut mich, dass Du ein treuer Leser meines Blogs bist. Das mit dem Picdump ist eine gute Idee. Ich schiebe das auf meiner To-Do List mal weiter nach oben. :)

    • http://www.feldstudie.net Torsten Feld

      und da ist er auch schon: http://www.feldstudie.net/2013/06/02/picdump-02-06-13/

      Viel Spaß damit :)

associated-texture
associated-texture