Start hintergrund-Prozess/daemon CGI-Skript
Ich versuche, starten Sie einen Hintergrundprozess, der von einem CGI-Skripte. Im Grunde, wenn ein Formular vorgelegt wird, das CGI-Skript werden, um dem Benutzer anzuzeigen, dass seine oder Ihre Anforderung verarbeitet wird, während der hintergrund-Skript führt die eigentliche Verarbeitung (weil die Verarbeitung neigt dazu, eine lange Zeit dauern.) Das problem, das ich bin vor ist, dass Apache nicht senden Sie die Ausgabe des übergeordneten CGI-Skript an den browser, bis das Kind Skript beendet wird.
Mir wurde gesagt von einem Kollegen, dass das, was ich tun möchte, ist unmöglich, weil es keine Möglichkeit gibt, um zu verhindern, dass der Apache vom warten auf den gesamten Prozess-Baum von einem CGI-Skript, um zu sterben. Allerdings habe ich auch gesehen, zahlreiche Referenzen rund um das web zu einem "Doppel-Gabel" - trick soll den job machen. Der trick beschrieben wird kurz und bündig in dieser Stack Overflow-Antwort, aber ich habe gesehen, ähnliche Codes an anderen stellen.
Hier eine kurze Skript, das ich schrieb, um zu testen, die Doppel-Gabel-trick in Python:
import os
import sys
if os.fork():
print 'Content-type: text/html\n\n Done'
sys.exit(0)
if os.fork():
os.setsid()
sys.exit(0)
# Second child
os.chdir("/")
sys.stdout.close()
sys.stderr.close()
sys.stdin.close()
f = open('/tmp/lol.txt', 'w')
while 1:
f.write('test\n')
Wenn ich von der shell aus, es tut genau das, was ich erwarten würde: das original-Skript und die ersten Nachkommen sterben, und der zweite Nachkomme läuft weiter bis er getötet manuell. Aber wenn ich auf Sie durch CGI, die Seite wird nicht geladen, bis ich töten, der zweite Nachkomme oder Apache tötet, weil es von der CGI-timeout. Ich habe auch versucht, anstelle des zweiten sys.exit(0)
mit os._exit(0)
, aber es gibt keinen Unterschied.
Was mache ich falsch?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nicht Gabel - führen Sie batch separat
Double-forking-Konzept ist eine Art "hack" für mich ist die Angabe, es nicht getan werden sollte :). Für CGI sowieso. Unter dem Allgemeinen Grundsatz, dass, wenn etwas zu schwer zu erreichen, Sie sind wahrscheinlich näher es der falsche Weg.
Zum Glück geben Sie die hintergrund-info, was Sie brauchen - ein CGI-Aufruf zu starten Verarbeitung erfolgt unabhängig und Rückgabe an den Aufrufer zurück. Na sicher - es gibt auch unix-Befehle, die genau das tun - Zeitplan-Befehl ausführen zu einer bestimmten Zeit (
at
) oder immer dann, wenn die CPU frei ist (batch
). Also statt:Und dort haben Sie es. Bedenken Sie, dass, wenn es irgendeine Ausgabe auf stdout/stderr, werden an den Benutzer gemailt (das ist gut für debugging, aber ansonsten Skript wohl ruhig sein sollte).
PS. ich erinnerte mich gerade, dass Windows auch die version von
at
, so dass mit geringfügigen änderungen von der Anrufung, die Sie haben können, dass die Arbeit unter apache auf windows zu (vs Gabel trick, der funktioniert nicht auf windows).PPS. stellen Sie sicher, dass der Prozess ausgeführt wird, der CGI ist nicht ausgeschlossen
/etc/at.deny
von scheduling von batch-jobsbeanstalkd
obwohl, ich denke, es wird schwer übertrieben. es bietet Erzeuger/Verbraucher-Entkopplung (wieat/batch
tun, mit Hilfe von cron-hinter den kulissen), aber es wird nicht starten, Verbraucher-Prozess, um die Arbeit zu tun, müssen Sie es selbst zu tun - aber wie? Wenn Sie ein Verbraucher, der immer ausgeführt wird, und die hängenden für die Arbeit in der Warteschlange, Sie haben einen Engpass, der nur eine Aufgabe auf einmal ausgeführt. Und was ist, wenn Sie mehrere parallel, dann hast du N Prozesse, die gestartet wurden, zu allen Zeiten und wartet auf die Warteschlange, unbehaglich.batch(1)
ist ein guter Weg, um dorthin zu gelangen :), aber beanstalk oder ähnliche tools ermöglichen den Aufenthalt "drinnen" die Programmiersprache für die latenten Aufgaben, die nicht brauchen, gehen durchfork()
+exec()
jedes mal, usw. Verschiedene Werkzeuge für verschiedene Aufgaben.os.system()
? 😀print "Content-type: text/plain\r\n\r\n"; os.system("/bin/cat /etc/motd")
gibt eine500
Fehler:malformed header from script. Bad header=Ubuntu 10.10
. Warum wird es versuchen, zu interpretieren, etwas nach der\r\n\r\n
als Header noch?at now <<< /path/to/script.py
von bash, es läuft wunderbar. Aber Wenn ich das python-Skript ruft dieos.system(at now <<< /path/to/script.py)
ich bekomme diese Fehlermeldungsh: 1: Syntax error: redirection unexpected
Ich denke, es gibt zwei Probleme:
setsid
ist an der falschen Stelle und tun gepufferte E /a-Operationen in einem der Transienten Kinder:Hast du den original-Prozess (Großeltern, Drucke "Erfolg"), die mittlere Elternteil, und der Enkel ("lol.txt").
Den
os.setsid()
Anruf durchgeführt wird, in der Mitte übergeordneten nach dem Enkel wurde gespawnt. Der Mitte haben die Eltern keinen Einfluss auf die Enkel der Sitzung, nachdem die Enkel angelegt wurde. Versuchen Sie dies:Dieser erzeugt eine neue session vor dem laichen der Enkel. Dann in der Mitte ein Elternteil stirbt, verlässt die Sitzung, ohne eine Prozess-Gruppenleiter, sicherzustellen, dass alle Anrufe öffnen Sie ein terminal wird scheitern, so dass Sie sicher, es gibt nie irgendeine Sperrung auf terminal-input-oder-output, oder senden unerwartete Signale an das Kind.
Beachten Sie, dass ich auch dazu bewegt, die
success
zu den Großeltern, es gibt keine Garantie die Kind wird zunächst nach dem Aufruffork(2)
, und Sie laufen Gefahr, das Kind würde hervorgebracht werden, und möglicherweise versuchen, zu schreiben Ausgabe auf standard-out oder standard-Fehler, bevor Sie die Mitte übergeordneten hätte eine chance gehabt, zu schreibensuccess
auf den remote-client.In diesem Fall die streams sind schnell geschlossen, aber immer noch das mischen von standard-IO-streams von mehreren Prozessen gebunden ist, zu geben, Schwierigkeitsgrad: halten Sie es alle in einem Prozess, wenn Sie können.
Bearbeiten ich habe ein seltsames Verhalten das ich nicht erklären kann:
Die Letzte Zeile, die
after second fork pid
nur angezeigt, wenn dieos.sleep(1)
Aufruf ist auskommentiert. Wenn der Anruf an Ort und Stelle bleiben, die Letzte Zeile nie im browser angezeigt wird. (Aber sonst ist alles der Inhalt wird an den browser.)inotify(7)
(vielleicht durch dieincron
Paket, vielleicht durchpython-pyinotify
oderpython-inotifyx
Bindungen), können Sie wahrscheinlich am Ende mit einer Lösung auf besser, als diese route -- aber ich bin zu neugierig, um herauszufinden, wie der Apache weiß, Wann Enkelkinder sterben, es scheint nicht, wie (a) sollte es möglich sein, (b) es ist vernünftig. So jetzt ist persönlichen. 🙂stdout
? ich nehme an, alle apache tut, ist Lesen aus demstdin
bis eof/Rohr ist gebrochen <- getroffen werden können als Indikator Kind getan wird. auch, wenn es sich (async?) Lesen mit timeout, das könnte sein, wie Ihrsleep(1)
macht es Geduld?daemonize()
code, vielleicht unnötig, da der Apache normalerweise nicht ein kontrollierendes terminal sowieso (-X
verhindern können, trennen). Ich hatte nicht gedacht, zu warten, bis das Rohr gebrochen ist, klingt ziemlich plausibel. 🙂 Vielen Dank!Ich würde nicht besagt gehen, über das problem auf diese Weise. Wenn Sie ausführen müssen einige Aufgaben asynchron, warum nicht verwenden eine Warteschlange wie beanstalkd anstatt zu versuchen, die Gabel aus der Aufgaben von der Anfrage? Es sind client-Bibliotheken für beanstalkd verfügbar für python.
Musste ich brechen die stdout als auch stderr wie diese:
Als in anderen Antworten angemerkt haben, ist es schwierig zu starten einen beständigen Prozess aus Ihrem CGI script, weil der Prozess muss sauber distanzieren sich von dem CGI-Programm. Ich habe festgestellt, dass ein großer Allzweck-Programm für diese ist daemon. Es kümmert sich um die chaotisch details mit geöffneten Datei-handles, Prozess-Gruppen, root-Verzeichnis, etc etc für Sie. Also das Muster wie ein CGI-Programm ist:
Den original-Beitrag beschreibt den Fall, wo Sie möchten, dass Ihre CGI-Programm schnell zurück, während der Laichzeit aus einem hintergrund-Prozess zu beenden, Umgang mit, dass eine Anfrage. Aber es ist auch der Fall, wenn Ihre web-Anwendung hängt von einer Laufenden Dienstleistung, die muss am Leben gehalten werden. (Andere Leute haben darüber gesprochen, über die Verwendung von beanstalkd zu handhaben Arbeitsplätze. Aber wie stellen Sie sicher, dass beanstalkd selbst aktiv ist?) Ein Weg dies zu tun ist, um den Dienst neu zu starten (wenn es unten ist) in das CGI-Skript. Dieser Ansatz macht Sinn in einer Umgebung, wo Sie haben nur begrenzte Kontrolle über den server und können sich nicht darauf verlassen, dass Dinge wie cron oder ein init.d-Mechanismus.
Ok, ich werde hinzufügen eine einfachere Lösung, wenn Sie nicht brauchen, um starten Sie ein anderes Skript aber weiterhin das gleiche zu tun, den langen Prozess in den hintergrund. Dies wird Ihnen geben eine wartende Nachricht sofort gesehen durch den AUFTRAGGEBER, und fahren Sie den server-Verarbeitung, selbst wenn der Kunde töten der browser-Sitzung:
Ich gelesen habe das halbe Internet nach einer Woche ohne Erfolg auf diese, schließlich habe ich versucht zu testen, ob es einen Unterschied zwischen
sys.stdout.close()
undos.close(sys.stdout.fileno())
und es ist ein riesige: Die erste hat nicht alles tun, während das zweite geschlossen, das Rohr aus dem web-server und komplett getrennt von den Kunden. Die Gabel ist nur notwendig, weil der webserver zu töten, seine Prozesse nach einer Weile und Ihr langer Prozess, der vermutlich mehr Zeit braucht, um abzuschließen.Gibt es Situationen, wo die Weitergabe der Arbeit aus, um einen daemon oder cron ist nicht angemessen. Manchmal Sie wirklich brauchen, Gabel, lassen Sie die Eltern verlassen (halten Apache glücklich) und lassen Sie etwas langsam geschehen in das Kind.
Was für mich gearbeitet: Wenn fertig Generierung der web-Ausgabe, und vor der Gabel:
fflush(stdout) und schließen(0), enge(1), enge(2); //in den Prozess, BEVOR SIE GABEL
Dann fork() und haben die Eltern sofort exit(0);
Das Kind dann WIEDER nicht
close(0), enge(1), enge(2);
und auch ein
setsid();
...und bekommt dann mit, was es braucht, um zu tun.
Warum Sie benötigen, um Sie zu schließen, in der das Kind, obwohl Sie geschlossen wurden, in der ursprünglichen Prozess im Voraus ist verwirrend für mich, aber das ist, was funktioniert. Es nicht ohne den 2. Satz schließt. Dieser wurde unter Linux (auf einem raspberry pi).
Ich habe nicht versucht, mit
fork
aber ich habe erreicht, was Sie Fragen bei der Ausführung einersys.stdout.flush()
nach der original-Nachricht, bevor Sie den Prozess im hintergrund.d.h.
some_processing()
dauert eine Stunde, wird Ihr web-Servers wird Sie schnell aus der open-Prozesse (Apache preforkMaxClients
variable), um eingehende Anfragen. Seien Sie vorsichtig. 🙂Mein Kopf tut immer noch weh, dass man auf. Ich habe versucht, alle Möglichkeiten zu nutzen, Ihren code mit Gabel und stdout schließen, nulling-oder nichts, aber nichts funktionierte. Der unvollendete Prozess-Ausgangsdaten die Anzeige hängt davon ab, webserver (Apache oder andere) - Konfiguration, und in meinem Fall war es nicht eine option, es zu ändern, also versucht mit "Transfer-Encoding: chunked;chunk=CRLF" und "sys.stdout.flush()" hat nicht funktioniert entweder. Hier ist die Lösung, die schließlich gearbeitet.
Kurz gesagt, verwenden Sie so etwas wie:
Ich benutze den "X" - parameter, um die Unterscheidung zwischen Elternteil und Kind, weil ich call das gleiche Skript für beide, aber Sie tun können, es einfacher durch den Aufruf von einem anderen script. Wenn ein vollständiges Beispiel nützlich sein, Fragen Sie bitte nach.
Für thous, die
"sh: 1: Syntax error: redirection unexpected"
mit den auf/charge Lösung versuchen Sie es mit etwas wie dieses:Stellen Sie sicher, dass der at-Befehl installiert ist und der Benutzer die Anwendung ausführen, ins-nicht in /etc/at.verweigern