Node ist darauf ausgelegt, E/A-Vorgänge effizient abzuwickeln. Sie sollten sich jedoch darüber im Klaren sein, dass einige Programmtypen für dieses Modell nicht geeignet sind. Wenn Sie beispielsweise vorhaben, Node zur Bewältigung einer CPU-intensiven Aufgabe zu verwenden, kann es sein, dass Sie die Ereignisschleife verstopfen und dadurch die Reaktionsfähigkeit des Programms verringern. Die Alternative besteht darin, CPU-intensive Aufgaben auf einen separaten Prozess auszulagern und so die Ereignisschleife freizugeben. Mit Node können Sie einen Prozess erzeugen und den neuen Prozess zu einem untergeordneten Prozess seines übergeordneten Prozesses machen. In Node kann der untergeordnete Prozess in zwei Richtungen mit dem übergeordneten Prozess kommunizieren, und in gewissem Umfang kann der übergeordnete Prozess auch den untergeordneten Prozess überwachen und verwalten.
Eine andere Situation, in der Sie einen Unterprozess verwenden müssen, ist, wenn Sie einfach einen externen Befehl ausführen und Node den Rückgabewert des Befehls erhalten lassen möchten. Sie können beispielsweise einen UNIX-Befehl, ein Skript oder andere Befehle ausführen, die nicht direkt in Node ausgeführt werden können.
In diesem Kapitel erfahren Sie, wie Sie externe Befehle ausführen, untergeordnete Prozesse erstellen und mit ihnen kommunizieren und untergeordnete Prozesse beenden. Der Sinn besteht darin, Ihnen zu zeigen, wie Sie eine Reihe von Aufgaben außerhalb des Node-Prozesses erledigen.
Externen Befehl ausführen
Wenn Sie einen externen Shell-Befehl oder eine ausführbare Datei ausführen müssen, können Sie das Modul child_process verwenden und es wie folgt importieren:
exec(command,callback);
// Anmerkung des Übersetzers: Wenn Sie Windows verwenden, können Sie zu Windows-Befehlen wie dir wechseln, die nicht noch einmal beschrieben werden
});
Wenn ein Fehler auftritt, ist der erste Parameter eine Instanz der Error-Klasse. Wenn der erste Parameter keinen Fehler enthält, enthält der zweite Parameter stdout die Standardausgabe des Befehls. Der letzte Parameter enthält eine Fehlerausgabe im Zusammenhang mit dem Befehl.
Listing 8-1 zeigt ein komplexeres Beispiel für die Ausführung externer Befehle
LISTING 8-1: Externe Befehle ausführen (Quellcode: Chapter8/01_external_command.js)
In der vierten Zeile übergeben wir „cat *.js | wc -l“ als ersten Parameter an exec. Sie können auch jeden anderen Befehl ausprobieren, sofern Sie den Befehl in der Shell verwendet haben.
Übergeben Sie dann als zweiten Parameter eine Rückruffunktion, die aufgerufen wird, wenn ein Fehler auftritt oder der untergeordnete Prozess beendet wird.
Sie können vor der Rückruffunktion auch einen dritten optionalen Parameter übergeben, der einige Konfigurationsoptionen enthält, wie zum Beispiel:
Zeitüberschreitung: 1000,
killSignal: „SIGKILL“
};
//…
});
1.cwd – Aktuelles Verzeichnis, Sie können das aktuelle Arbeitsverzeichnis angeben.
2. Kodierung – das Kodierungsformat des Ausgabeinhalts des untergeordneten Prozesses. Der Standardwert ist „utf8“, was der UTF-8-Kodierung entspricht. Wenn die Ausgabe des untergeordneten Prozesses nicht utf8 ist, können Sie diesen Parameter verwenden, um ihn festzulegen. Die unterstützten Codierungsformate sind:
Puffer zum Verarbeiten, Kodieren und Dekodieren von Binärdaten verwenden“.
1.timeout – Befehlsausführungszeitlimit in Millisekunden, der Standardwert ist 0, was bedeutet, dass es keine Begrenzung gibt, bis der untergeordnete Prozess endet.
2.maxBuffer – Geben Sie die maximale Anzahl von Bytes an, die vom stdout-Stream und stderr-Stream ausgegeben werden dürfen. Wenn der Maximalwert erreicht ist, wird der untergeordnete Prozess beendet. Der Standardwert ist 200*1024.
3.killSignal – Ein Beendigungssignal, das an den untergeordneten Prozess gesendet wird, wenn das Zeitlimit überschritten wird oder der Ausgabepuffer seine maximale Größe erreicht. Der Standardwert ist „SIGTERM“, wodurch ein Beendigungssignal an den untergeordneten Prozess gesendet wird. Dieser geordnete Ansatz wird normalerweise zum Beenden von Prozessen verwendet. Bei Verwendung des SIGTERM-Signals kann der Prozess es verarbeiten oder das Standardverhalten des Signalprozessors nach dem Empfang überschreiben. Wenn der Zielprozess dies benötigt, können Sie ihm gleichzeitig andere Signale (z. B. SIGUSR1) übergeben. Sie können auch ein SIGKILL-Signal senden, das vom Betriebssystem verarbeitet wird und das sofortige Beenden des untergeordneten Prozesses erzwingt. In diesem Fall werden keine Bereinigungsvorgänge des untergeordneten Prozesses durchgeführt.
1.evn – Gibt die Umgebungsvariablen an, die an den untergeordneten Prozess übergeben werden. Der Standardwert ist null, was bedeutet, dass der untergeordnete Prozess die Umgebungsvariablen aller übergeordneten Prozesse erbt, bevor er erstellt wird.
Hinweis: Mit der Option killSignal können Sie ein Signal in Form einer Zeichenfolge an den Zielprozess senden. Signale liegen in Form von Zeichenfolgen in Node vor. Im Folgenden finden Sie eine Liste von UNIX-Signalen und entsprechenden Standardoperationen:
Möglicherweise möchten Sie dem untergeordneten Prozess einen erweiterbaren Satz übergeordneter Umgebungsvariablen bereitstellen. Wenn Sie das Objekt „process.env“ direkt ändern, ändern Sie die Umgebungsvariablen aller Module im Node-Prozess, was zu großen Problemen führt. Die Alternative besteht darin, ein neues Objekt zu erstellen und alle Parameter in „process.env“ zu kopieren, siehe Beispiel 8-2:
LISTING 8-2: Verwenden Sie parametrisierte Umgebungsvariablen, um Befehle auszuführen (Quellcode: Chapter8/02_env_vars_augment.js)
envCopy['CUSTOM ENV VAR1'] = 'some value';
envCopy['CUSTOM ENV VAR2'] = 'ein anderer Wert';
exec(‘ls –la’,{env: envCopy}, function(err,stdout,stderr){
If(err){ throw err;
console.log(‘stdout:’, stdout);
console.log(‘stderr:’,stderr);
}
Das obige Beispiel erstellt eine envCopy-Variable zum Speichern von Umgebungsvariablen. Sie kopiert zunächst die Umgebungsvariablen des Node-Prozesses aus process.env, fügt dann einige Umgebungsvariablen hinzu oder ersetzt sie, die geändert werden müssen, und verwendet schließlich envCopy als Umgebung Variable Argumente werden an die Exec-Funktion übergeben und der externe Befehl wird ausgeführt.
Denken Sie daran, dass Umgebungsvariablen zwischen Prozessen über das Betriebssystem übergeben werden und alle Arten von Umgebungsvariablenwerten den untergeordneten Prozess in Form von Zeichenfolgen erreichen. Wenn der übergeordnete Prozess beispielsweise die Zahl 123 als Umgebungsvariable enthält, erhält der untergeordnete Prozess „123“ als Zeichenfolge.
Das folgende Beispiel erstellt zwei Node-Skripte im selben Verzeichnis: parent.js und child.js. Das erste Skript ruft das zweite auf:
LISTING 8-3: Der übergeordnete Prozess legt Umgebungsvariablen fest (chapter8/03_environment_number_parent.js)
exec('node child.js', {env: {number: 123}}, function(err, stdout, stderr) {
if (err) { throw err;
console.log('stdout:n', stdout);console.log('stderr:n', stderr);
});
Beispiel 8-4: Parsen von Umgebungsvariablen für untergeordnete Prozesse (chapter8/04_environment_number_child.js)
number = parseInt(number, 10);
console.log(typeof(number)); // → "number"
Nummer
stderr:
Untergeordneten Prozess generieren
Wie Sie sehen, können Sie die Funktion child_process.exec() verwenden, um einen externen Prozess zu starten und Ihre Rückruffunktion aufzurufen, wenn der Prozess endet. Dies ist sehr einfach zu verwenden, hat aber auch einige Nachteile:1. Zusätzlich zur Verwendung von Befehlszeilenparametern und Umgebungsvariablen kann exec() nicht mit untergeordneten Prozessen kommunizieren
2. Die Ausgabe des untergeordneten Prozesses wird zwischengespeichert, sodass Sie sie nicht streamen können, da möglicherweise nicht mehr genügend Speicher vorhanden ist
Untergeordneten Prozess erstellen
Sie können die Funktion child_process.spawn verwenden, um einen neuen untergeordneten Prozess zu erstellen, siehe Beispiel 8-5:Beispiel 8-5: Einen untergeordneten Prozess erzeugen. (Kapitel 8/05_spawning_child.js)
// Generieren Sie einen untergeordneten Prozess, um den Befehl „tail -f /var/log/system.log“ auszuführen
var child = spawn('tail', ['-f', '/var/log/system.log']);
Der obige Code generiert einen Unterprozess zum Ausführen des Tail-Befehls und verwendet „-f“ und „/bar/log/system.log“ als Parameter. Der Befehl tail überwacht die Datei /var/log/system.og (falls vorhanden) und gibt alle angehängten neuen Daten an den Standardausgabestream stdout aus. Die Spawn-Funktion gibt ein ChildProcess-Objekt zurück, bei dem es sich um ein Zeigerobjekt handelt, das die Zugriffsschnittstelle des realen Prozesses kapselt. In diesem Beispiel weisen wir diesen neuen Deskriptor einer Variablen namens child zu.
Daten von untergeordneten Prozessen abhören
Jedes untergeordnete Prozesshandle, das das stdout-Attribut enthält, verwendet die Standardausgabe stdout des untergeordneten Prozesses als Stream-Objekt. Sie können das Datenereignis an dieses Stream-Objekt binden, sodass immer dann der entsprechende Rückruf erfolgt, wenn ein Datenblock verfügbar ist Funktion, siehe Beispiel unten:
child.stdout.on(‘data’,function(data){
console.log(‘tail output: ‘ data);
});
Immer wenn der untergeordnete Prozess Daten an stdout ausgibt, wird der übergeordnete Prozess benachrichtigt und gibt die Daten an die Konsole aus.
Zusätzlich zur Standardausgabe verfügt der Prozess über einen weiteren Standardausgabestream: den Standardfehlerstream. Dieser Stream wird normalerweise zur Ausgabe von Fehlerinformationen verwendet.
Wenn in diesem Beispiel die Datei /var/log/system.log nicht vorhanden ist, gibt der Endprozess eine Meldung ähnlich der folgenden aus: „/var/log/system.log: Keine solche Datei oder kein solches Verzeichnis“ Durch die Überwachung des stderr-Streams wird der übergeordnete Prozess benachrichtigt, wenn ein solcher Fehler auftritt.
Der übergeordnete Prozess kann den Standardfehlerstrom wie folgt abhören:
console.log('tail error output:', data);
});
Das stderr-Attribut ist wie stdout auch ein schreibgeschützter Stream. Immer wenn der untergeordnete Prozess Daten an den Standardfehlerstream ausgibt, wird der übergeordnete Prozess benachrichtigt und gibt die Daten aus.
Daten an untergeordneten Prozess senden
Zusätzlich zum Empfangen von Daten aus dem Ausgabestrom des untergeordneten Prozesses kann der übergeordnete Prozess über die Eigenschaft childPoces.stdin auch Daten in die Standardeingabe des untergeordneten Prozesses schreiben und so Daten an und von dem untergeordneten Prozess senden.
Der untergeordnete Prozess kann die Standardeingabedaten über den schreibgeschützten Stream „process.stdin“ überwachen. Beachten Sie jedoch, dass Sie zuerst den Standardeingabestream fortsetzen müssen, da dieser standardmäßig angehalten ist.
Beispiel 8-6 erstellt ein Programm, das die folgenden Funktionen enthält:
1. 1 Anwendung: Eine einfache Anwendung, die Ganzzahlen von der Standardeingabe empfangen, addieren und das hinzugefügte Ergebnis dann an den Standardausgabestream ausgeben kann. Als einfacher Rechendienst simuliert diese Anwendung den Node-Prozess als externen Dienst, der bestimmte Arbeiten ausführen kann.
2. Testen Sie den Client einer Anwendung, senden Sie zufällige Ganzzahlen und geben Sie dann die Ergebnisse aus. Wird verwendet, um zu demonstrieren, wie der Node-Prozess einen untergeordneten Prozess erzeugt und ihn dann bestimmte Aufgaben ausführen lässt.
Erstellen Sie eine Datei mit dem Namen plus_one.js mit dem Code in Beispiel 8-6 unten:
Beispiel 8-6: 1 Anwendung (chapter8/06_plus_one.js)
Im obigen Code warten wir auf Daten aus dem stdin-Standardeingabestream, gehen wir davon aus, dass es sich um eine Ganzzahl handelt, analysieren sie in eine Ganzzahlvariable, addieren dann 1 und geben das Ergebnis an den Standardausgabestream aus .
Sie können dieses Programm mit dem folgenden Befehl ausführen:
Nach der Ausführung wartet das Programm auf die Eingabe. Wenn Sie eine Ganzzahl eingeben und die Eingabetaste drücken, wird auf dem Bildschirm eine um 1 addierte Zahl angezeigt.
Sie können das Programm beenden, indem Sie Strg-C drücken.
Ein Testkunde
Jetzt müssen Sie einen Knotenprozess erstellen, um die von der vorherigen „1 Anwendung“ bereitgestellten Computerdienste zu nutzen.
Erstellen Sie zunächst eine Datei mit dem Namen plus_one_test.js. Der Inhalt ist in Beispiel 8-7 dargestellt:
Beispiel 8-7: Test 1-Anwendung (chapter8/07_plus_one_test.js)
Ein Unterprozess zum Ausführen von „1 Anwendung“ wird von der ersten bis zur vierten Zeile gestartet, und dann wird die Funktion setInterval verwendet, um einmal pro Sekunde die folgenden Vorgänge auszuführen:
1.. Erstellen Sie eine neue Zufallszahl kleiner als 10000
2. Übergeben Sie diese Nummer als Zeichenfolge an den untergeordneten Prozess
3. Warten Sie, bis der untergeordnete Prozess mit einer Zeichenfolge
antwortet
4. Da Sie jeweils nur das Berechnungsergebnis einer Zahl erhalten möchten, müssen Sie child.stdout.once anstelle von child.stdout.on verwenden. Wenn letzteres verwendet wird, wird jede Sekunde eine Rückruffunktion für das Datenereignis registriert. Jede registrierte Rückruffunktion wird ausgeführt, wenn die Standardausgabe des untergeordneten Prozesses Daten empfängt, sodass Sie feststellen, dass dasselbe Berechnungsergebnis ausgegeben wird. Oftmals ist dieses Verhalten eindeutig falsch.
Benachrichtigung erhalten, wenn der untergeordnete Prozess beendet wird
Wenn der untergeordnete Prozess beendet wird, wird das Exit-Ereignis ausgelöst. Beispiel 8-8 zeigt, wie man darauf hört:
Beispiel 8-8: Abhören des Exit-Ereignisses des untergeordneten Prozesses (chapter8/09_listen_child_exit.js)
// Wenn der untergeordnete Prozess beendet wird:
child.on('exit', function(code) {
console.log('untergeordneter Prozess mit Code beendet ' code);
});
In den letzten Zeilen des fettgedruckten Codes verwendet der übergeordnete Prozess das Exit-Ereignis des untergeordneten Prozesses, um auf dessen Exit-Ereignis zu warten. Wenn das Ereignis auftritt, zeigt die Konsole die entsprechende Ausgabe an. Der Exit-Code des untergeordneten Prozesses wird als erster Parameter an die Callback-Funktion übergeben. Einige Programme verwenden einen Exit-Code ungleich Null, um bestimmte Fehlerbedingungen darzustellen. Wenn Sie beispielsweise versuchen, den Befehl „ls –al click filename.txt“ auszuführen, die Datei jedoch nicht im aktuellen Verzeichnis vorhanden ist, erhalten Sie den Exit-Code 1, siehe Beispiel 8-9:
Beispiel 8-9: Ermitteln Sie den Exit-Code des untergeordneten Prozesses (chapter8/10_child_exit_code.js)
In diesem Beispiel löst das Exit-Ereignis die Callback-Funktion aus und übergibt ihr als ersten Parameter den Exit-Code des untergeordneten Prozesses. Wenn der untergeordnete Prozess abnormal beendet wird, weil er durch ein Signal beendet wurde, wird der entsprechende Signalcode als zweiter Parameter an die Rückruffunktion übergeben, z. B. Beispiel 8-10:
LISTE 8-10: Erhalten Sie das Exit-Signal des untergeordneten Prozesses (Kapitel 8/11_child_exit_signal.js)
In diesem Beispiel wird ein untergeordneter Prozess gestartet, um den Schlafvorgang für 10 Sekunden auszuführen, aber vor 10 Sekunden wird ein SIGKILL-Signal an den untergeordneten Prozess gesendet, was zu der folgenden Ausgabe führt:
Signal senden und Vorgang beenden
In diesem Teil erfahren Sie, wie Sie Signale verwenden, um untergeordnete Prozesse zu verwalten. Signale sind eine einfache Möglichkeit für einen übergeordneten Prozess, mit einem untergeordneten Prozess zu kommunizieren oder ihn sogar zu beenden.
Unterschiedliche Signalcodes stellen unterschiedliche Bedeutungen dar. Es gibt viele Signale, einige der häufigsten werden zum Beenden von Prozessen verwendet. Wenn ein Prozess ein Signal empfängt, mit dem er nicht umgehen kann, wird das Programm abnormal unterbrochen. Einige Signale werden von untergeordneten Prozessen verarbeitet, während andere nur vom Betriebssystem verarbeitet werden können.
Im Allgemeinen können Sie die Methode child.kill verwenden, um ein Signal an den untergeordneten Prozess zu senden. Das SIGTERM-Signal wird standardmäßig gesendet:
Sie können ein bestimmtes Signal auch senden, indem Sie eine Zeichenfolge übergeben, die das Signal als einzigen Parameter der Kill-Methode identifiziert:
Es ist zu beachten, dass der Name dieser Methode zwar kill lautet, das gesendete Signal jedoch nicht unbedingt den untergeordneten Prozess beendet. Wenn der untergeordnete Prozess das Signal verarbeitet, wird das Standardsignalverhalten überschrieben. In Node geschriebene Unterprozesse können die Definition von Signalhandlern wie folgt umschreiben:
Da Sie nun den SIGUSR2-Signalhandler definiert haben, wird Ihr Prozess, wenn er das SIGUSR2-Signal erneut empfängt, nicht beendet, sondern gibt den Satz „Habe ein SIGUSR2-Signal erhalten“ aus. Mit diesem Mechanismus können Sie eine einfache Möglichkeit entwerfen, mit dem untergeordneten Prozess zu kommunizieren und ihn sogar zu befehlen. Diese Methode ist zwar nicht so umfangreich wie die Standardeingabe, aber viel einfacher.
Zusammenfassung
In diesem Kapitel haben wir gelernt, die Methode child_process.exec zum Ausführen externer Befehle zu verwenden. Diese Methode verwendet keine Befehlszeilenparameter, sondern übergibt die Parameter an den untergeordneten Prozess, indem sie Umgebungsvariablen definiert.
Sie haben auch gelernt, wie Sie externe Befehle aufrufen, indem Sie die Methode child_process.spawn aufrufen, um einen untergeordneten Prozess zu erzeugen. Auf diese Weise können Sie Eingabeströme und Ausgabeströme verwenden, um mit dem untergeordneten Prozess zu kommunizieren, oder Signale verwenden, um mit und zu kommunizieren Beenden Sie den untergeordneten Prozess.