Einen Perl-Daemon erstellen, der 24/7 läuft und aus Named Pipes liest

7

Ich versuche, einen Log-Analysator mit Perl zu machen. Der Analyzer würde rund um die Uhr im Hintergrund auf einem AIX-Server laufen und von Pipes lesen, von denen syslog Logs anleitet (aus dem gesamten Netzwerk). Grundsätzlich:

%Vor%

Zum Beispiel möchte ich, dass mein Daemon auf mail [email protected] aller Protokolle, die in named pipe C geschrieben sind, konfiguriert werden kann. Dazu nehme ich an, dass der Daemon einen Hash haben muss (neu für Perl, aber dies scheint eine geeignete Datenstruktur zu sein), der im laufenden Betrieb geändert werden könnte und ihm sagen würde, was mit jeder Pipe zu tun ist / p>

Ist das möglich? Oder sollte ich eine .conf Datei in /etc erstellen, um die Informationen zu speichern. Etwas wie das:

%Vor%

Also wird alles von A an [email protected] gesendet und alles von B wird in einer Protokolldatei gespeichert (wie es normalerweise ist) UND es wird an [email protected]

gesendet

Da ich zum ersten Mal Perl benutze und ich zum ersten Mal einen Daemon erstelle, ist es für mich trotzdem möglich, dies zu tun, während ich an KISS Rektor? Gibt es irgendwelche Konventionen, an die ich mich halten sollte? Wenn Sie meinen Mangel an Wissen bei der Antwort berücksichtigen könnten, wäre es sehr hilfreich.

    
n0pe 28.07.2011, 18:26
quelle

2 Antworten

18

Ich werde einen Teil Ihrer Frage behandeln: Wie schreibe ich ein lang laufendes Perl-Programm, das sich mit IO beschäftigt.

Die effizienteste Möglichkeit, ein Perl-Programm zu schreiben, das viele gleichzeitige E / A-Operationen verarbeitet, ist die Verwendung einer Ereignisschleife. Dadurch können wir Handler für Ereignisse schreiben, wie "eine Zeile auf der Named Pipe erschien" oder "die E-Mail wurde erfolgreich gesendet" oder "wir haben SIGINT erhalten". Entscheidend ist, dass wir eine beliebige Anzahl dieser Event-Handler in einem Programm zusammenstellen können. Dies bedeutet, dass Sie "Multitasking" durchführen können, aber trotzdem problemlos den Status zwischen den Aufgaben teilen können.

Wir verwenden das AnyEvent -Rahmenwerk. Es ermöglicht uns das Schreiben von Event-Handlern namens Watcher, die mit jeder Event-Schleife funktionieren, die von Perl unterstützt wird. Es ist Ihnen wahrscheinlich egal, welche Ereignisschleife Sie verwenden, daher spielt diese Abstraktion wahrscheinlich für Ihre Anwendung keine Rolle. Aber es wird uns erlauben, vorbeschriebene Ereignishandler, die auf CPAN verfügbar sind, wiederzuverwenden; AnyEvent :: SMTP zur Bearbeitung von E-Mails, AnyEvent :: Subprozess , um mit untergeordneten Prozessen zu interagieren, AnyEvent :: Handle mit den Rohren umgehen, und so weiter.

Die grundlegende Struktur eines AnyEvent-basierten Daemons ist sehr einfach. Sie erstellen einige Beobachter, geben Sie die Ereignisschleife ein, und ... das ist es; Das Event-System macht alles andere. Schreiben wir zunächst ein Programm, das alle fünf Sekunden "Hallo" ausgibt.

Wir beginnen mit dem Laden von Modulen:

%Vor%

Dann erstellen wir einen Zeitbeobachter oder einen "Timer":

%Vor%

Beachten Sie, dass wir den Timer einer Variablen zuweisen. Dies hält den Timer am Leben, solange sich $t im Bereich befindet. Wenn wir undef $t sagten, würde der Timer abgebrochen und der Callback würde nie aufgerufen werden.

Über Rückrufe, das ist sub { ... } nach cb => , und so behandeln wir Ereignisse. Wenn ein Ereignis eintritt, wird der Rückruf aufgerufen. Wir machen unser Ding, kehren zurück und die Ereignisschleife ruft bei Bedarf weitere Callbacks auf. Sie können in Rückrufen alles tun, was Sie möchten, einschließlich Abbrechen und Erstellen anderer Beobachter. Machen Sie keinen blockierenden Anruf wie system("/bin/sh long running process") oder my $line = <$fh> oder sleep 10 . Alles, was blockiert, muss von einem Beobachter getan werden; Andernfalls kann die Ereignisschleife keine anderen Handler ausführen, während sie auf die Beendigung dieser Aufgabe wartet.

Jetzt wo wir einen Timer haben, müssen wir nur die Ereignisschleife eingeben. In der Regel wählen Sie eine Ereignisschleife aus, die Sie verwenden möchten, und geben sie auf die spezielle Weise ein, die in der Dokumentation der Ereignisschleife beschrieben wird. EV ist ein guter Tipp, und Sie geben ihn ein, indem Sie EV::loop() aufrufen. Aber wir werden AnyEvent entscheiden lassen, welche Ereignisschleife verwendet werden soll, indem wir AnyEvent->condvar->recv schreiben. Mach dir keine Sorgen, was das tut; Es ist ein Idiom, das bedeutet "in die Ereignisschleife eintreten und niemals zurückkehren". (Sie werden eine Menge über Condition-Variablen oder Condvars erfahren, wenn Sie über AnyEvent lesen. Sie sind schön für Beispiele in der Dokumentation und in Unit-Tests, aber Sie wollen sie wirklich nie in Ihrem Programm verwenden. Wenn Sie Wenn du sie in einer .pm -Datei verwendest, machst du etwas sehr falsches. Also tu so, als würden sie nicht existieren und du wirst von Anfang an extrem sauberen Code schreiben von vielen CPAN-Autoren!)

Also, nur der Vollständigkeit halber:

%Vor%

Wenn Sie dieses Programm ausführen, wird "Hello" alle fünf Sekunden gedruckt, bis das Universum endet, oder Sie töten es wahrscheinlich mit dem Steuerelement c. Was ist nett daran ist, dass Sie andere Dinge in diesen fünf Sekunden zwischen dem Drucken von "Hallo" tun können, und Sie tun es nur durch Hinzufügen weiterer Beobachter.

Also, jetzt zum Lesen von Rohren. AnyEvent macht dies mit seinem AnyEvent :: Handle-Modul sehr einfach. AnyEvent :: Handle kann Verbindungen zu Sockets oder Pipes herstellen und ruft einen Callback auf, sobald Daten zum Lesen verfügbar sind. (Es kann auch nicht blockierende Schreibvorgänge, TLS und andere Dinge tun. Aber das interessiert uns jetzt nicht.)

Zuerst müssen wir eine Pipe öffnen:

%Vor%

Dann wickeln wir es mit einem AnyEvent :: Handle um. Nach dem Erstellen des Handle-Objekts verwenden wir es für alle Operationen auf dieser Pipe. Sie können total $fh vergessen, AnyEvent :: Handle wird es direkt berühren.

%Vor%

Jetzt können wir $h verwenden, um Zeilen aus der Pipe zu lesen, wenn sie verfügbar sind:

%Vor%

Dies ruft den Callback auf, der "Got a line" ausgibt, wenn die nächste Zeile verfügbar wird. Wenn Sie mit dem Lesen von Zeilen fortfahren möchten, müssen Sie dafür sorgen, dass sich die Funktion selbst wieder in die Lesewarteschlange einschaltet, wie zum Beispiel:

%Vor%

Dies liest Zeilen und ruft $handle_line->() für jede Zeile auf, bis die Datei geschlossen ist. Wenn du früh aufhören willst, zu lesen, ist das einfach ... mach einfach nicht push_read in diesem Fall. (Sie müssen nicht auf Zeilenebene lesen; Sie können darum bitten, dass Ihr Callback aufgerufen wird, sobald Bytes verfügbar sind.Aber das ist komplizierter und dem Leser als Übung überlassen.)

Nun können wir das alles in einen Daemon einbinden, der das Lesen der Pipes behandelt. Was wir tun wollen, ist: Erstellen Sie einen Handler für Linien, öffnen Sie die Rohre und behandeln Sie die Linien, und richten Sie schließlich einen Signalhandler ein, um das Programm sauber zu verlassen. Ich empfehle einen OO-Ansatz für dieses Problem; Machen Sie jede Aktion ("handle Zeilen aus der Zugriffsprotokolldatei") eine Klasse mit einer start und stop -Methode, instanziieren Sie eine Menge von Aktionen, richten Sie einen Signalhandler ein, um die Aktionen sauber zu stoppen, starten Sie alle Aktionen und dann Geben Sie die Ereignisschleife ein. Das ist eine Menge Code, der nicht wirklich mit diesem Problem zu tun hat, also machen wir etwas einfacheres. Aber denken Sie daran, während Sie Ihr Programm entwerfen.

%Vor%

Nun haben Sie ein Programm, das eine Zeile aus einer beliebigen Anzahl von Pipes liest, jede Zeile ausgibt, die auf einer beliebigen Pipe empfangen wurde (mit dem Pfad zur Pipe vorangestellt) und sauber beendet, wenn Sie Strg-C drücken!

    
jrockway 29.07.2011, 22:48
quelle
2

Erste Vereinfachung - behandeln Sie jede benannte Pipe in einem separaten Prozess. Das bedeutet, dass Sie für jede benannte Pipe einen Perl-Prozess ausführen, aber dann müssen Sie ereignisbasierte E / A oder Threading nicht verwenden.

Nehmen wir an, dass Sie die Konfigurationsdaten (Pfad zur Named Pipe, zu verwendende E-Mail-Adresse usw.) einfach in der Befehlszeile übergeben müssen, z. B .:

%Vor%

Funktioniert das für Sie?

Um sicherzustellen, dass die Daemons aufbleiben, schauen Sie sich ein Paket an, wie D. J. Bernsteins daemontools oder supervisord (keuch! Ein Python-Paket).

Jedes dieser Pakete zeigt Ihnen, wie Sie Ihre RC-Skripte so konfigurieren, dass sie beim Booten des Computers starten.

    
perlman 28.07.2011 20:58
quelle

Tags und Links