Laden...

Best practice für Nachrichten-Logger: Viele Nachrichten performant *und* ohne Datenverlust speichern

Erstellt von andski vor 10 Jahren Letzter Beitrag vor 10 Jahren 2.057 Views
A
andski Themenstarter:in
9 Beiträge seit 2013
vor 10 Jahren
Best practice für Nachrichten-Logger: Viele Nachrichten performant *und* ohne Datenverlust speichern

Hallo,

ich will aus Interesse einen Logger implementieren. Folgendes muss gehen:* Nachrichten in eine Datei schreiben.

  • Möglichen Datenverlust minimieren. Das heisst, im Falle eines Absturzes sollen nicht die letzten 200 Meldungen verloren gehen. Der Logger muss also fast jede Nachricht sofort in die Datei schreiben.
  • GUI darf in ihrer Performance nicht beeinflusst werden.

Mein bisheriger Ansatz besteht darin, dass die Logger-Klasse einen Writer-Thread startet, der 10 mal pro sekunde alle Nachrichten in der Queue in die Datei schreibt. Probehalber habe ich das mit einer Schleife mit 100.000 Iterationen getestet. Jeder Durchlauf ruft einmal die log()-Funktion auf. Im Hintergrund läuft der Writer-Thread und schreibt die Datei. Das Ganze dauert etwa 0.5 sekunden.

Offenkundiges Problem: In der Zeit, in der der Writer-Thread schläft wird die Queue gefüllt. Stürzt das Programm jetzt ab, sind die letzten x-Tausend Meldungen weg. Nicht Gut.

Um dies zu "lösen" müsste also jede Nachricht sofort in die Datei geschrieben werden. Das ging bisher jedoch nur auf Kosten der Performance. 100.000 Einträge dauerten somit ca. 15 Sekunden.

Ich habe mir mal eine kleine Test-Applikation gebaut, die log4net verwendet. Für 100.000 Logs brauchte ich so ebenfalls 0.5 Sekunden. Allerdings weiss ich nicht wie das dort implementiert wurde.

Mir ist bewusst, dass 100.000 Nachrichten in so extrem kurzer Zeit in den meisten Fällen kaum geloggt werden müssen. Jedoch interessiert es mich, wie man an diese Sache heran gehen kann, um bei sehr guter Performance möglichst jede Meldung sofort zu loggen.

Bitte deshalb um Rat und bin dankbar für Hinweise.

Gruß
Andski

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo andski,

du könntest den Logger als separaten Prozess implementieren. Die eigentliche Anwendung würde die Nachrichten dann z.B. per TCP/IP an den Logger schicken. Der würde deine performante Logging-Variante implementieren. Natürlich müsste nach jeder Nachricht die TCP/IP-Verbindung geflushed werden. Stürzt die eigentliche Anwendung ab, kann der Logger trotzdem noch in Ruhe die letzten Nachrichten in die Datei schreiben. Bei einem Absturz des Loggers wären die letzten Nachrichten zwar weg, aber so ein Absturz ist dafür viel unwahrscheinlicher.

herbivore

A
andski Themenstarter:in
9 Beiträge seit 2013
vor 10 Jahren

Hallo,

an so eine Lösung habe ich noch gar nicht gedacht.
Aber gibt es auch eine Möglichkeit, den Logger als Klasse direkt im Program zu implementieren, sodass bei einer sehr großen Nachrichtenflut möglichst jede Nachricht sofort in die Datei geschrieben wird? Die Probleme hierbei sind also einmal dass sich schneller neue Nachrichten anhäufen, als geschrieben werden können (es sei denn man schreibt eine Menge Nachrichten mit einem Mal, was aber wieder zu Datenverlust führen kann).

Gruß
andski

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo andski,

den Trade Off, wenn du den Logger innerhalb des zu loggenden Prozesses implementierst, hast du doch schon geschildert. Mir ist nicht klar, was du dir da noch für Alternativen versprichst. Also ich wüsste nicht, was es zwischen a) Nachrichten sammeln und auf einen Rutsch schreiben und dafür den möglichen Verlust, der gesammelten und noch nicht geschriebenen Nachrichten hinnehmen oder b) jede Nachricht einzelne schreiben und dafür die schlechtere Performance hinnehmen, noch für Alternativen geben soll.

Natürlich ist das ständige Schreiben einzelner kleiner Datenhäppchen auf eine mechanisch-magnetische Festplatte per se langsam. Eine Ramdisk könnte das beschleunigen.

herbivore

W
955 Beiträge seit 2010
vor 10 Jahren

Hi,

Datenbanken haben ein ähnliches Problem:
* es finden viele parallele Lese- und Schreibvorgänge statt
* ein Schreibprozess schreibt die modifizierten, im Speicher liegenden Blöcke zurück wenn die IO-Last es erlaubt
* damit Transaktionen aber nicht verloren gehen (was committed ist bleibt auch nach einem Crash erhalten) müssen alle Änderungen erst einmal in einem hocheffizienten Log geschrieben werden um bei einem Crash daraus den korrekten Datenzustand wieder herstellen zu können (steal-no-force Strategie)

Möglicherweise könnte man sich da etwas abschauen wie es die verschiedenen DBMS es implementieren.

W
872 Beiträge seit 2005
vor 10 Jahren

Erstmal würde ich einen Standardlogger wie log4net benutzen.
Bei log4net koennte dann ein udp oder Tcp Appender benutzt werden kombiniert mit einem eigenen Programm, dass die Daten von dem Appender dann persistent macht und zum Beispiel dann als ein eigener Prozess mit eigenen Queues, eigenem Flush etc. arbeitet.

156 Beiträge seit 2010
vor 10 Jahren

Um dies zu "lösen" müsste also jede Nachricht sofort in die Datei geschrieben werden. Das ging bisher jedoch nur auf Kosten der Performance. 100.000 Einträge dauerten somit ca. 15 Sekunden.

Da Du keinen Quelltext postest, vermute ich mal:
1.log() - Aufruf 1.Log-Datei öffnen 1.Seek an das Ende 1.Meldung schreiben 1.Datei schließen

Das kostet richtig Ressourcen. Richtig wäre: Log-Datei am Programm Anfang öffnen, und nach jeder Meldung gleich flushen, Log-Datei nie schließen. Läuft bei mir nach diesem Prinzip seit Jahren.

Damit wirst Du auch keine Meldung verlieren.

Hand, mogel

6.911 Beiträge seit 2009
vor 10 Jahren

Hallo andski,

wie herbivore schon vorgeschlagen hat, würde ich auch eine weiteren Prozess für das eigentliche Loggen verwenden und für die Kommunikation MSMQ verwenden, da so die Verlust-Wahrscheinlichkeit von Meldungen geringer ist. Siehe hierzu z.B. HowTo: Messaging mit MSMQ, ein Einstieg.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

A
350 Beiträge seit 2010
vor 10 Jahren

Hallo gfoidl,

genau das würde ich auch vorschlagen, wenn jmd in MSMQ die Nachrichten nicht abholt (Prozess stürzt ab) bleiben die in der Pipeline.
Dann kann der Prozess neu starten und alles abgeholt werden.

A
andski Themenstarter:in
9 Beiträge seit 2013
vor 10 Jahren

Hallo und Danke an eure Vorschläge.

@mogel: Es läuft momentan so, wie Du es vermutet hast: Datei öffnen, schreiben, schließen.
Die Variante mit dem Prozess klingt noch einen Tick sicherer. Ich werde es mal so machen.

Gruß
andski