Freunde, die mit js vertraut sind, wissen, dass js 是单线程
. In Node wird ein Single-Thread-Modell mit mehreren Prozessen übernommen. Aufgrund der Single-Thread-Beschränkung von JavaScript müssen wir auf Multi-Core-Servern oft mehrere Prozesse starten, um die Serverleistung zu maximieren.
Node.js-Prozesscluster können zum Ausführen mehrerer Node.js-Instanzen verwendet werden, die die Arbeitslast auf ihre Anwendungsthreads verteilen können. Wenn keine Prozessisolation erforderlich ist, verwenden Sie stattdessen das Modul worker_threads
, das die Ausführung mehrerer Anwendungsthreads innerhalb einer einzelnen Node.js-Instanz ermöglicht.
Node führte nach Version V0.8 das Clustermodul ein,一个主进程(master) 管理多个子进程(worker) 的方式实现集群
.
Das Cluster-Modul erleichtert die Erstellung untergeordneter Prozesse, die Server-Ports gemeinsam nutzen.
Die unterste Ebene des Clusters ist das Modul child_process. Zusätzlich zum Senden normaler Nachrichten können auch zugrunde liegende Objekte
TCP
,UDP
usw. gesendet werden.cluster
Clustermodul ist eine kombinierte Anwendungchild_process
und desnet
. Beim Start des Clusters wird der TCP-Server intern gestartet und der Dateideskriptor des TCP-Server-Sockets an den Arbeitsprozess gesendet.
In der cluster
-Modulanwendung一个主进程只能管理一组工作进程
. Sein Betriebsmodus ist nicht so flexibel wie der des child_process
-Moduls, aber stabiler:
const Cluster = require('cluster') und komplexe
.isMaster
identifiziert den Hauptprozess, Node<16.isPrimary.isPrimary
den Hauptprozess, Node>16.isWorker.isWorker
den Unterprozess .worker ist.worker
. Verweis auf Prozessobjekt [im untergeordneten Prozess].workers
speichert den Hash des aktiven Worker-Prozessobjekts mit dem id
Feld als Schlüssel. Dies erleichtert das Durchlaufen aller Worker-Prozesse. Es ist nur im Hauptprozess verfügbar. cluster.wokers[id] === worker
[im Hauptprozess].settings
ist ein schreibgeschütztes Cluster-Konfigurationselement. Nach dem Aufruf der Methode .setupPrimary() oder .fork() enthält dieses Einstellungsobjekt die Einstellungen, einschließlich Standardwerte. Zuvor ein leeres Objekt. Dieses Objekt sollte nicht manuell geändert oder eingestellt werden.cluster.settings
:– „execArgv“ <string[]>Eine Liste von Zeichenfolgenparametern, die an die ausführbare Datei Node.js übergeben werden. **Standard:** `process.execArgv`. - `exec` <string> Dateipfad zur Arbeitsprozessdatei. **Standard:** `process.argv[1]`. - `args` <string[]> String-Argumente, die an den Arbeitsprozess übergeben werden. **Standard:** `process.argv.slice(2)`. - `cwd` <string>Das aktuelle Arbeitsverzeichnis des Arbeitsprozesses. **Standard:** „undefiniert“ (vom übergeordneten Prozess geerbt). - „serialization“ <string>Gibt den Serialisierungstyp an, der zum Senden von Nachrichten zwischen Prozessen verwendet wird. Mögliche Werte sind „json“ und „advanced“. **Standard:** `false`. - `silent` <boolean>Gibt an, ob die Ausgabe an die Standardeingabe und -ausgabe des übergeordneten Prozesses gesendet werden soll. **Standard:** `false`. - „stdio“ <Array> konfiguriert die Standardeingabe und -ausgabe des erzeugten Prozesses. Da das Clustermodul zur Ausführung auf IPC angewiesen ist, muss diese Konfiguration einen „ipc“-Eintrag enthalten. Wenn diese Option bereitgestellt wird, überschreibt sie „silent“. - „uid“ <Nummer> legt die Benutzer-ID des Prozesses fest. - „gid“ <Nummer> legt die Gruppen-ID des Prozesses fest. - `inspectPort` <Nummer> |. <Funktion> Legt den Inspektor-Port für den Arbeitsprozess fest. Dies kann eine Zahl oder eine Funktion sein, die keine Parameter akzeptiert und eine Zahl zurückgibt. Standardmäßig verfügt jeder Arbeitsprozess über einen eigenen Port, beginnend mit dem „process.debugPort“ des Hauptprozesses und aufsteigend. - `windowsHide` <boolean> Blendet das erzeugte Prozesskonsolenfenster aus, das normalerweise auf Windows-Systemen erstellt wird. **Standard:** `false`.
.fork([env])
erzeugt einen neuen Arbeitsprozess [im Hauptprozess].setupPrimary([settings])
Node>16.setupMaster([settings])
wird verwendet, um das Standardverhalten von „Fork“ zu ändern. Nach der Verwendung werden die Einstellungen in cluster.settings
angezeigt. Alle Einstellungsänderungen wirken sich nur auf zukünftige Aufrufe von .fork()
aus, nicht auf bereits laufende Arbeitsprozesse. Die oben genannten Standardwerte gelten nur für den ersten Aufruf. Knoten ist kleiner als 16 [Im Hauptprozess].disconnect([callback])
Wird aufgerufen, wenn alle Arbeitsprozesse die Verbindung trennen und Handles schließen [Im Hauptprozess]Um den Cluster stabiler und robuster zu machen, wird auch cluster
-Modul verwendet macht viele Ereignisse verfügbar:
'message'
-Ereignis, das ausgelöst wird, wenn der Cluster-Master-Prozess eine Nachricht von einem beliebigen Arbeitsprozess empfängt.'exit'
-Ereignis: Wenn ein Arbeitsprozess stirbt, löst das Clustermodul das 'exit'
-Ereignis aus.cluster.on('exit', (worker, code, signal) => { console.log('Arbeiter %d gestorben (%s). Neustart...', worker.process.pid, Signal ||. Cluster.fork(); });
'listening'
-Ereignis: Nach dem Aufruf von listen()
vom Worker-Prozess löst cluster
im Hauptprozess auch das „Listening“-Ereignis aus, wenn das 'listening'
'listening'
-Ereignis auf dem Server ausgelöst wird.Cluster.on('listening', (Worker, Adresse) => { console.log( `Ein Worker ist jetzt mit ${address.address}:${address.port} verbunden`); });
'fork'
-Ereignis: Wenn ein neuer Arbeitsprozess erzeugt wird, löst das Clustermodul das 'fork'
-Ereignis aus.cluster.on('fork', (worker) => { timeouts[worker.id] = setTimeout(errorMsg, 2000); });
'setup'
Ereignis, das jedes Mal ausgelöst wird, wenn .setupPrimary()
aufgerufen wird.disconnect
wird ausgelöst, nachdem die Verbindung zum IPC-Kanal des Arbeitsprozesses getrennt wurde. Wenn der Worker-Prozess normal beendet wird, abgebrochen wird oder die Verbindung manuell trennt,Cluster.on('disconnect', (worker) => { console.log(`Der Worker #${worker.id} hat die Verbindung getrennt`); });
Das Worker
Objekt enthält alle öffentlichen Informationen und Methoden des Worker-Prozesses. Im Hauptprozess können Sie cluster.workers
verwenden, um es abzurufen. In einem Arbeitsprozess können Sie es mit cluster.worker
abrufen.
.id
-Prozess-Identifikation Jeder neue Worker-Prozess erhält seine eigene eindeutige ID. Diese ID wird in id
gespeichert. Wenn ein Arbeitsprozess aktiv ist, ist dies der Schlüssel, der ihn in cluster.workers
indiziert..process
Alle Arbeitsprozesse werden mit child_process.fork()
erstellt und das von dieser Funktion zurückgegebene Objekt wird als .process
gespeichert. Im Worker-Prozess wird der globale process
gespeichert..send(message[, sendHandle[, options]][, callback])
sendet eine Nachricht an den Worker-Prozess oder den Hauptprozess, und Sie können wählen, ob Sie ein Handle verwenden möchten. Im Hauptprozess wird dadurch eine Nachricht an einen bestimmten Arbeitsprozess gesendet. Es ist dasselbe wie ChildProcess.send()
. Im Worker-Prozess wird dadurch eine Nachricht an den Hauptprozess gesendet. Es ist dasselbe wie process.send()
..destroy()
.kill([signal])
Diese Funktion beendet den Arbeitsprozess. Die Funktion kill()
beendet den Arbeitsprozess, ohne auf eine ordnungsgemäße Trennung zu warten. Sie verhält sich genauso wie worker.process.kill()
. Aus Gründen der Abwärtskompatibilität wird diese Methode mit dem Alias worker.destroy()
versehen..disconnect([callback])
wird an den Arbeitsprozess gesendet, was dazu führt, dass dieser sein eigenes .disconnect()
aufruft, das alle Server herunterfährt, auf 'close'
-Ereignisse auf diesen Servern wartet und dann den IPC-Kanal trennt..isConnect()
Diese Funktion gibt true
zurück, wenn der Arbeitsprozess über seinen IPC-Kanal mit seinem Hauptprozess verbunden ist, andernfalls false
. Arbeitsprozesse stellen nach der Erstellung eine Verbindung zu ihrem Masterprozess her..isDead()
Diese Funktion gibt true
zurück, wenn der Arbeitsprozess beendet wurde (aufgrund des Beendens oder des Empfangs eines Signals). Andernfalls wird false
zurückgegeben.Um den Cluster stabiler und robuster zu machen, stellt das cluster
Modul auch viele Ereignisse bereit: das
'message'
-Ereignis im Worker-Prozess.Cluster.workers[id].on('message', messageHandler);
'exit'
Ereignis: Wenn ein Worker-Prozess stirbt, löst当前worker工作进程
das 'exit'
Ereignis aus.if (cluster.isPrimary) { const worker = cluster.fork(); worker.on('exit', (code, signal) => { if (signal) { console.log(`Worker wurde durch Signal getötet: ${signal}`); } else if (code !== 0) { console.log(`Worker mit Fehlercode beendet: ${code}`); } anders { console.log('worker success!'); } }); }
'listening'
Ereignis: Rufen Sie listen()
vom Worker-Prozess aus auf, um den aktuellen Worker-Prozess abzuhören.cluster.fork().on('listening', (address) => { // Der Worker-Prozess lauscht });
disconnect
Ereignis, das ausgelöst wird, nachdem der IPC-Kanal des Worker-Prozesses getrennt wurde. Wenn der Worker-Prozess normal beendet wird, beendet wird oder die Verbindung manuell trennt, istdie Cluster.fork().on('disconnect', () => {//Beschränkt auf
das
auf dem aktuellen Worker-Objekt});
.send()
(a.send bedeutet das Senden einer Nachricht an eine Send-Methode) zum Senden von Nachrichten und zum Abhören von message
, um Informationen zu sammeln. Dies wird vom cluster模块
durch die Integration EventEmitter
implementiert. Es ist auch ein einfaches Beispiel für die Kommunikation zwischen Prozessen auf der offiziellen Website
process.on('message')
, process.send()
child.on('message')
, child.send()
#cluster.isMaster #cluster.fork() #cluster.workers # cluster.workers[id].on('message', messageHandler); # Cluster.workers[id].send(); #process.on('message', messageHandler); #process.send(); const Cluster = require('cluster'); const http = require('http'); # Hauptprozess if (cluster.isMaster) { // Verfolgen Sie HTTP-Anfragen console.log(`Primary ${process.pid} läuft`); sei numReqs = 0; // Anfragen zählen Funktion messageHandler(msg) { if (msg.cmd && msg.cmd === 'notifyRequest') { numReqs += 1; } } // Worker starten und auf Nachrichten warten, die notifyRequest enthalten // Multiprozess starten (Anzahl der CPU-Kerne) // Worker-Prozess erzeugen. const numCPUs = require('os').cpus().length; for (let i = 0; i < numCPUs; i++) { console.log(i) Cluster.fork(); } // Cluster-Worker-Hauptprozess kommuniziert mit untergeordneten Prozessen für (const id in cluster.workers) { // ***Ereignisse von untergeordneten Prozessen abhören cluster.workers[id].on('message', messageHandler); // ***Cluster.workers[id].send({ an den untergeordneten Prozess senden Typ: 'masterToWorker', von: 'Meister', Daten: { Zahl: Math.floor(Math.random() * 50) } }); } cluster.on('exit', (worker, code, signal) => { console.log(`worker ${worker.process.pid} ist gestorben`); }); } anders { # Untergeordnete Prozesse // Worker-Prozesse können jede TCP-Verbindung gemeinsam nutzen // In diesem Beispiel handelt es sich um einen HTTP-Server // Worker-Prozesse haben einen http-Server. http.Server((req, res) => { res.writeHead(200); res.end('Hallo Weltn'); //******! ! ! ! Benachrichtigen Sie den Master über die Anfrage! ! ! ! ! ! ******* //****** Send process.send({ cmd: 'notifyRequest' }); //****** Abhören von process.on('message', function(message) { // xxxxxxx }) }).listen(8000); console.log(`Worker ${process.pid} gestartet`); }
Die Kommunikation zwischen NodeJS-Prozessen umfasst nur die Weitergabe von Nachrichten und überträgt nicht tatsächlich Objekte.
Vor dem Senden der Nachricht wird die Nachricht von der Methode send()
in ein Handle zusammengestellt und die Nachricht wird von JSON.stringify
serialisiert. Das heißt, beim Übergeben des Handles wird nicht das gesamte Objekt übergeben Übertragen über den IPC-Kanal. Sie sind alle Zeichenfolgen und werden nach der Übertragung über JSON.parse
in Objekten wiederhergestellt.
Der Code enthält app.listen(port)
. Warum können beim Forken mehrere Prozesse denselben Port abhören?
Der Grund dafür ist, dass der Hauptprozess das Handle eines zum Hauptprozess gehörenden Serviceobjekts über die send()-Methode an mehrere Unterprozesse sendet, sodass jeder Unterprozess nach dem Wiederherstellen des Handles dasselbe Serviceobjekt erhält. Wenn das Netzwerk eine Anfrage an den Server stellt, ist der Prozessdienst präventiv, sodass beim Abhören desselben Ports keine Ausnahme verursacht wird.
#master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); for (let i=0; i<cpus.length; i++) { const worker = fork('worker.js'); console.log('Worker-Prozess erstellt, pid: %s ppid: %s', worker.pid, process.pid); }
# worker.js const http = require('http'); http.createServer((req, res) => { res.end('Ich bin Arbeiter, pid: ' + Process.pid + ', ppid: ' + Process.ppid); }).listen(3000);
Wenn die Konsole im obigen Codebeispiel
node master.js
kann nur ein Worker Port 3000 abhören, und der Rest gibtError: listen EADDRINUSE :::3000
aus.
发送句柄
zwischen Prozessen nach Version v0.5.9/**. * http://nodejs.cn/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback * Nachricht * sendHandle */ subprocess.send(message, sendHandle)
Nachdem der IPC-Kanal zwischen dem übergeordneten und dem untergeordneten Prozess eingerichtet wurde, wird die Nachricht über die Sendemethode des Unterprozessobjekts gesendet.二个参数sendHandle 就是句柄,可以是TCP套接字、TCP服务器、UDP套接字等
Um das oben genannte Multiprozess-Portbelegungsproblem zu lösen, übergeben wir den Socket des Hauptprozesses an den untergeordneten Prozess.
#master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); const server = require('net').createServer(); server.listen(3000); Process.title = 'node-master' for (let i=0; i<cpus.length; i++) { const worker = fork('worker.js'); # Übergeben Sie das Handle worker.send('server', server); console.log('Worker-Prozess erstellt, pid: %s ppid: %s', worker.pid, process.pid); }
// worker.js Arbeiter lassen; Process.title = 'node-worker' process.on('message', function (message, sendHandle) { if (message === 'server') { worker = sendHandle; worker.on('connection', function (socket) { console.log('Ich bin Arbeiter, pid: ' + Process.pid + ', ppid: ' + Process.ppid) }); } });
Stellen Sie sicher, dass die Konsole node master.js
ausführt
Wenn Sie cluster
verstehen, wissen Sie, dass untergeordnete Prozesse über cluster.fork()
erstellt werden. Unter Linux stellt das System nativ die fork
-Methode bereit. Warum entscheidet sich Node also dafür, cluster模块
selbst zu implementieren, anstatt direkt die native Methode des Systems zu verwenden? Die Hauptgründe sind die folgenden zwei Punkte:
Der Fork-Prozess überwacht denselben Port, was zu Portbelegungsfehlern führt.
Es gibt keinen Lastausgleich zwischen den Fork-Prozessen, was z. B. zum Thundering-Herd-Phänomen führen
cluster模块
Das erste Problem besteht darin, festzustellen, ob der aktuelle Prozess master进程
ist. Wenn nicht, wird er als Fork- worker进程
dargestellt und überwacht den Port nicht.
Als Antwort auf die zweite Frage verfügt cluster模块
über eine integrierte Lastausgleichsfunktion. master进程
ist dafür verantwortlich, den Port abzuhören, um Anforderungen zu empfangen, und weist sie dann über den Planungsalgorithmus den entsprechenden worker进程
zu (Standard ist). Round-Robin, der Planungsalgorithmus kann über die Umgebungsvariable NODE_CLUSTER_SCHED_POLICY
geändert werden.
Wenn der Code eine Ausnahme auslöst, die nicht abgefangen wird, wird der Prozess beendet. Zu diesem Zeitpunkt stellt Node.js process.on('uncaughtException', handler)
zum Abfangen bereit, aber wenn eine Wenn der Worker-Prozess auf eine nicht abgefangene Ausnahme stößt, befindet er sich bereits in einem unsicheren Zustand. Zu diesem Zeitpunkt sollten wir den Prozess ordnungsgemäß beenden:
+---------+ +---------+ |. Arbeiter | +---------+ +----+----+ |. uncaughtException | +----------------+ | |. |. +--------+ |. <---------+ | |. |. +----+----+ |. trennen |. einen neuen Worker forken | +----------> + ----------------------- -> | |. warte... | |. Ausfahrt | +---------> | |. | sterben | |. | |. |.
Wenn ein Prozess einen Absturz oder OOM verursacht und vom System abgebrochen wird, haben wir immer noch die Möglichkeit, den Prozess weiter ausführen zu lassen Der aktuelle Prozess wird direkt beendet und der Master forkt sofort einen neuen Worker.
Das Modul „child_process“ bietet die Möglichkeit, untergeordnete Prozesse abzuleiten, was einfach执行cmd命令的能力
. Standardmäßig stdin、 stdout 和stderr 的管道会在父Node.js 进程和衍生的子进程之间建立
. Diese Pipelines verfügen über eine begrenzte (und plattformspezifische) Kapazität. Wenn der untergeordnete Prozess beim Schreiben in stdout diesen Grenzwert überschreitet und keine Ausgabe erfasst wird, blockiert der untergeordnete Prozess und wartet darauf, dass der Pipe-Puffer weitere Daten akzeptiert. Dies ist das gleiche Verhalten wie bei einer Pipe in der Shell. Wenn die Ausgabe nicht verbraucht wird, verwenden Sie die Option { stdio: 'ignore' }.
const cp = require('child_process');
Der über die API erstellte untergeordnete Prozess hat keine notwendige Verbindung mit dem übergeordneten Prozess.
Zum Erstellen untergeordneter Prozesse werden 4 asynchrone Methoden verwendet: fork, exec, execFile, spawn
Node
fork(modulePath, args)
: Wird verwendet, wenn Sie einen Node-Prozess als unabhängigen Prozess ausführen möchten, sodass die Berechnungsverarbeitung und der Dateideskriptor vom Node-Hauptprozess getrennt sind (Kopieren eines untergeordneten Prozesses).Non-Node
spawn(command, args)
: Verarbeiten Sie einige Probleme. Verwenden Sie execFile(file, args[, callback]), wenn es viele Unterprozess-E/As gibt oder wenn der Prozess eine große Ausgabemenge hatexecFile(file, args[, callback])
Verwenden Sie es, wenn Sie nur ein externes Programm ausführen müssen Die Ausführungsgeschwindigkeit ist hoch und die Verarbeitung von Benutzereingaben ist relativ sicher.exec(command, options)
: Wird verwendet, wenn Sie direkt auf den Shell-Befehl des Threads zugreifen möchten. Achten Sie unbedingt aufdie drei vom Benutzer eingegebenen execSync
. execFileSync
, spawnSync
Die anderen drei Methoden sind Erweiterungen von spawn()
.
. Denken Sie daran, dass der abgeleitete untergeordnete Node.j-Prozess unabhängig ist Der übergeordnete Prozess, mit Ausnahme des zwischen beiden eingerichteten IPC-Kommunikationskanals. Jeder Prozess verfügt über einen eigenen Speicher und eine eigene V8-Instanz
.
Erstellen Sie beispielsweise zwei Dateien, worker.js und master.js, in einem Verzeichnis:
# child.js const t = JSON.parse(process.argv[2]); console.error(`untergeordneter Prozess t=${JSON.stringify(t)}`); process.send({hello:`son pid=${process.pid} bitte gib Papa Prozess pid=${process.ppid} hallo`}); process.on('message', (msg)=>{ console.error(`untergeordneter Prozess msg=${JSON.stringify(msg)}`); });
# parent.js const {fork} = require('child_process'); for(let i = 0; i < 3; i++){ const p = fork('./child.js', [JSON.stringify({id:1,name:1})]); p.on('message', (msg) => { console.log(`messsgae from child msg=${JSON.stringify(msg)}`, ); }); p.send({hello:`Grüße von Papa ${process.pid} Process id=${i}`}); }
Starten Sie parent.js über node parent.js
und überprüfen Sie dann die Anzahl der Prozesse über ps aux | grep worker.js
Wir können feststellen, dass die Anzahl der Prozesse im Idealfall der Anzahl der CPU-Kerne entspricht und jeder Prozess einen verwendet CPU-Kern.
Dies ist der klassische Master-Worker-Modus (Master-Slave-Modus)
Tatsächlich ist das Forken eines Prozesses teuer, und der Zweck des Kopierens eines Prozesses besteht darin, die CPU-Ressourcen voll auszunutzen. Daher verwendet NodeJS einen ereignisgesteuerten Ansatz für einen einzelnen Thread, um das Problem der hohen Parallelität zu lösen.
Anwendbare Szenarien <br/>Wird im Allgemeinen für zeitaufwändige Szenarien verwendet und mithilfe von Knoten implementiert, z. B. beim Herunterladen von Dateien.
Fork kann Multithread-Downloads implementieren: Teilen Sie die Datei in mehrere Blöcke auf, dann lädt jeder Prozess einen Teil herunter und fügt sie schließlich zusammen.
const cp = require('child_process'); // Der erste Parameter ist der Name oder Pfad der auszuführenden ausführbaren Datei. Hier ist Echo cp.execFile('echo', ['hello', 'world'], (err, stdout, stderr) => { if (err) { console.error(err); console.log('stdout: ', stdout); console.log('stderr: ', stderr); });
Anwendbare Szenarien <br/> Eher geeignet für Aufgaben mit geringem Overhead und mehr Aufmerksamkeit auf Ergebnisse, wie z. B.
wird hauptsächlich zum Ausführen einer Shell-Methode verwendet, und spawn ist es wird immer noch intern aufgerufen, aber es gibt ein maximales Cache-Limit.
const cp = require('child_process'); cp.exec(`cat ${__dirname}/messy.txt | sort | uniq`, (err, stdout, stderr) => { console.log(stdout); });
Anwendbare Szenarien <br/>Eher geeignet für Aufgaben mit geringem Overhead und größerem Augenmerk auf Ergebnisse, wie z. B.
Einzelaufgabe.
const cp = require('child_process'); const child = cp.spawn('echo', ['hello', 'world']); child.on('error', console.error); # Die Ausgabe ist ein Stream, der an den Hauptprozess stdout und die Konsole ausgegeben wird child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr);
Multitasking-Verkettung
const cp = require('child_process'); const path = require('path'); const cat = cp.spawn('cat', [path.resolve(__dirname, 'messy.txt')]); const sort = cp.spawn('sort'); const uniq = cp.spawn('uniq'); #Die Ausgabe ist ein Stream cat.stdout.pipe(sort.stdin); sort.stdout.pipe(uniq.stdin); uniq.stdout.pipe(process.stdout);
Anwendbare Szenarien
Spawn ist Streaming und eignet sich daher für zeitaufwändige Aufgaben wie das Ausführen von npm install und das Drucken des Installationsprozesses
wird ausgelöst, nachdem der Prozess beendet wurde und der Standard-Eingabe- und Ausgabestream (sdtio) des Der untergeordnete Prozess wurde 'close'
. Dieses Ereignis unterscheidet sich vom exit
, da mehrere Prozesse denselben stdio-Stream gemeinsam nutzen können.
Parameter:
Frage: Muss der Code existieren?
(Aus den Kommentaren zum Code geht das offenbar nicht hervor.) Wenn Sie beispielsweise kill
verwenden, um den untergeordneten Prozess zu beenden, wie lautet der Code?
-Parameter:
Code, Signal, wenn der untergeordnete Prozess von selbst beendet wird, dann ist code
der Exit-Code, andernfalls ist er null;
Wenn der untergeordnete Prozess durch ein Signal beendet wird, ist das signal
das Signal zum Beenden des Prozesses, andernfalls ist es null.
Einer der beiden darf nicht null sein.
Dinge zu beachten :
Wenn das exit
-Ereignis ausgelöst wird, ist der stdio-Stream des untergeordneten Prozesses möglicherweise noch geöffnet. (Szenario?) Darüber hinaus überwacht nodejs die Signale SIGINT und SIGTERM. Das heißt, wenn nodejs diese beiden Signale empfängt, wird es nicht sofort beendet, sondern führt zunächst einige Aufräumarbeiten durch und wirft dann diese beiden Signale erneut aus. (Visuell gesehen kann js zu diesem Zeitpunkt Aufräumarbeiten durchführen, z. B. das Schließen der Datenbank usw.)
SIGINT
: Interrupt, Programmbeendigungssignal, normalerweise ausgegeben, wenn der Benutzer STRG + C drückt, um den Vordergrundprozess zu benachrichtigen, den Prozess zu beenden.
SIGTERM
: Beenden, Programmendesignal. Dieses Signal kann blockiert und verarbeitet werden und wird normalerweise verwendet, um das normale Beenden des Programms zu erfordern. Der Shell-Befehl kill generiert dieses Signal standardmäßig. Wenn das Signal nicht beendet werden kann, versuchen wir es mit SIGKILL (erzwungene Beendigung).
Wenn die folgenden Dinge passieren, wird ein Fehler ausgelöst. Wenn ein Fehler ausgelöst wird, kann der Exit ausgelöst werden oder auch nicht. (Das Herz ist gebrochen)
wird ausgelöst, wenn process.send()
zum Senden einer Nachricht verwendet wird.
Parameter :
message
ist ein JSON-Objekt oder ein primitiver Wert; sendHandle
, ein net.Socket-Objekt oder ein net.Server-Objekt (mit Cluster vertraute Schüler sollten damit vertraut sein)
: Legen Sie es fest, wenn Sie .disconnected()
aufrufen zu falsch. Stellt dar, ob Nachrichten vom untergeordneten Prozess empfangen oder Nachrichten an den untergeordneten Prozess gesendet werden können.
.disconnect() : Schließen Sie den IPC-Kanal zwischen dem übergeordneten Prozess und dem untergeordneten Prozess. Wenn diese Methode aufgerufen wird, wird das disconnect
ausgelöst. Wenn es sich bei dem untergeordneten Prozess um eine Knoteninstanz handelt (erstellt durch child_process.fork()), kann process.disconnect()
auch aktiv innerhalb des untergeordneten Prozesses aufgerufen werden, um den IPC-Kanal zu beenden.
reagiert auf Single-Threading-Probleme.
werden vom Node-Prozess belegt. Der Kern des 7-Thread
die v8-Engine. Nach dem Start des Knotens wird eine Instanz von v8 erstellt
von
单线程
, aber die Host-Umgebung von Javascript, egal ob Node oder Browser, ist Multithreaded.
Warum ist Javascript Single-Threaded?
Dieses Problem muss mit dem Browser beginnen. Stellen Sie sich vor, dass die DOM-Operation nur auf eine einzige Weise ausgeführt werden kann, wenn mehrere Threads auf demselben DOM ausgeführt werden Vermeiden Sie DOM-Rendering-Konflikte. In der Browserumgebung schließen sich der UI-Rendering-Thread und die JS-Ausführungs-Engine gegenseitig aus. Wenn einer ausgeführt wird, wird der andere angehalten.
Process.env.UV_THREADPOOL_SIZE = 64
worker_threads
um Node echte Multithreading-Funktionen bereitzustellen.. isMainThread, parentPort, workerData, Thread-ID, Nachrichtenkanal, MessagePort, Arbeitnehmer } = require('worker_threads'); Funktion mainThread() { for (sei i = 0; i < 5; i++) { const worker = new Worker(__filename, { workerData: i }); worker.on('exit', code => { console.log(`main: worker gestoppt mit Exit-Code ${code}`); }); worker.on('message', msg => { console.log(`main: Erhalte ${msg}`); worker.postMessage(msg + 1); }); } } Funktion workerThread() { console.log(`worker: workerDate ${workerData}`); parentPort.on('message', msg => { console.log(`worker: Erhalte ${msg}`); }), parentPort.postMessage(workerData); } if (isMainThread) { mainThread(); } anders { workerThread(); }
const affirm = require('assert'); const { Arbeitnehmer, Nachrichtenkanal, MessagePort, isMainThread, parentPort } = require('worker_threads'); if (isMainThread) { const worker = new Worker(__filename); const subChannel = new MessageChannel(); worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('message', (value) => { console.log('received:', value); }); } anders { parentPort.once('message', (value) => { behaupten(value.hereIsYourPort Instanz von MessagePort); value.hereIsYourPort.postMessage('der Arbeiter sendet dies'); value.hereIsYourPort.close(); }); }
Der Prozess ist die kleinste Einheit der Ressourcenzuweisung, und der Thread ist die kleinste Einheit der CPU-Planung.
IPC (Interprozesskommunikation) ist进程间通信
. Da jeder Prozess nach der Erstellung über einen eigenen unabhängigen Adressraum verfügt, besteht der Zweck der Implementierung von IPC darin, den Zugriff auf Ressourcen zwischen Prozessen zu teilen.
Es gibt viele Möglichkeiten, IPC zu implementieren: Pipes, Nachrichtenwarteschlangen, Semaphoren, Domain-Sockets und Node.js werden über Pipes implementiert.
Tatsächlich erstellt der übergeordnete Prozess zunächst einen IPC-Kanal und hört diesen IPC ab, bevor er den untergeordneten Prozess erstellt. Anschließend wird der untergeordnete Prozess über die Umgebungsvariable () mitgeteilt. NODE_CHANNEL_FD). Der untergeordnete Prozess wird gestartet. Zu diesem Zeitpunkt wird der IPC-Kanal gemäß dem Dateideskriptor verbunden, um eine Verbindung mit dem übergeordneten Prozess herzustellen.
Ein Handle ist eine Referenz, die zur Identifizierung einer Ressource verwendet werden kann. Es enthält den Dateiressourcendeskriptor, der auf das Objekt verweist.
Wenn wir mehrere Prozesse an einem Port überwachen möchten, können wir im Allgemeinen die Verwendung des Hauptprozessagenten in Betracht ziehen:
Diese Proxy-Lösung führt jedoch dazu, dass bei jedem Anforderungsempfang und bei jeder Proxy-Weiterleitung zwei Dateideskriptoren verwendet werden, und die Dateideskriptoren des Systems sind begrenzt. Dieser Ansatz beeinträchtigt die Skalierbarkeit des Systems.
Warum also Griffe verwenden? Der Grund dafür ist, dass der Aufbau der IPC-Kommunikation in tatsächlichen Anwendungsszenarien komplexere Datenverarbeitungsszenarien umfassen kann. Das Handle kann als zweiter optionaler Parameter der send()
Methode übergeben werden, was bedeutet, dass die Ressourcenkennung direkt übergeben werden kann Durch die Übertragung wird die Verwendung von Dateideskriptoren vermieden, die durch die oben erwähnte Proxy-Weiterleitung verursacht werden.
Die folgenden Handle-Typen unterstützen das Senden:
Nachdem der übergeordnete Prozess des verwaisten Prozesses einen untergeordneten Prozess erstellt hat, wird der übergeordnete Prozess beendet, jedoch ein oder mehrere untergeordnete Prozesse Prozesse, die dem übergeordneten Prozess entsprechen, sind noch am Leben. Dies wird durch das folgende Codebeispiel veranschaulicht.
# Worker.js const http = required ('http'); const server = http.createServer ((req, res) => { res.end ('Ich bin Arbeiter, PID:' + process.pid + ', ppid:' + process.ppid); // Notieren Sie die aktuelle Arbeitsprozess -PID und den übergeordneten Prozess PPID }); Arbeiter lassen; process.on ('meldung', Funktion (Nachricht, sendHandle) { if (message === 'server') { Worker = SendHandle; Worker.on ('Verbindung', Funktion (Socket) { server.emit ('Verbindung', Socket); }); } });
# Master.js const fork = fork = fork; const server = fordert ('net'). createServer (); Server.Listen (3000); const Worker = fork ('Worker.js'); Worker.Send ('Server', Server); console.log ('Erstellen von Arbeitern, PID: %S ppid: %s', Worker.pid, process.pid); process.exit (0);// Nach
dem
Erstellen des untergeordneten Prozesss wird der Hauptprozess.
Da der übergeordnete Prozess in Master.js beendet ist, zeigt der Aktivitätsmonitor nur den Arbeitsprozess an.
Überprüfen Sie erneut, ob die Konsolen -Anrufschnittstelle geöffnet wird. Sie können feststellen, dass der PPID, der dem Arbeiterprozess 5611 entspricht, 1 (für den Init -Prozess) ist und zu diesem Zeitpunkt zu einem Orphan -Prozess geworden ist.
Der Dämon -Prozess läuft im Hintergrund und ist nicht vom Terminal betroffen.
Studierende, die Node.js entwickeln, können node app.js
vertraut sein.前台运行模式
.
Wenn die Daemon -Prozessmethode verwendet wird, kann ich nach Ausführen node app.js
einen Serviceprozess in diesem Terminal starten, ich kann auch andere Dinge auf diesem Terminal tun, ohne sich gegenseitig zu beeinflussen.
Erstellen Sie einen untergeordneten Prozess
Erstellen Sie eine neue Sitzung im untergeordneten Prozess (rufen Sie die Systemfunktion SetSID an).
Ändern Sie das Arbeitsverzeichnis des untergeordneten
Prozess
options.detached
Ausgänge (die Systemschicht wird die SETSID -Methode aufrufen const spawn = fordert ('child_process'). Spawn; Funktion startDaemon () { const daemon = spawn ('node', ['Daemon.js'], { CWD: '/usr', abgelöst: wahr, Stdio: 'Ignorieren', }); console.log ('Dämon -Prozess startet übergeordnete Prozess PID: %s, Daemon Process PID: %s', process.pid, Daemon.pid); Daemon.unref (); } startdaemon ()
Die
Verarbeitungslogik in der Datei von Daemon.js startet einen Timer und führt sie alle 10 Sekunden aus, damit diese Ressource gleichzeitig nicht beendet wird.
des Kinderprozesses.const fs = require('fs'); const {console} = require ('console'); // benutzerdefinierte einfache Logger const logger = neue Konsole (fs.createwritestream ('./ stdout.log'), fs.createwritestream ('./ Stderr.log')); setInterval (function () { logger.log ('Daemon pid:', process.pid, ', ppid:', process.ppid);},
1000
*
10);
in der tatsächlichen Arbeit sind wir keine Unbekannten für Daemon-Prozesse wie PM2, Ei-Cluster usw. Das oben genannte ist nur eine einfache Demo, um den Dämon-Prozess zu erklären. Der Daemon -Prozess ist immer noch sehr hoch.
5.Was ist das
aktuelleprocess.cwd()
des
Aus dem Verzeichnis des übergeordneten Prozesses kann process.chdir()
Was macht es
zum Beispiel, wenn sie eine Datei über FS lesen, wenn sie auf einen relativen Pfad eingestellt ist, wird sie relativ zum Verzeichnis durchsucht, in dem der aktuelle Prozess gestartet wird. Das richtige Ergebnis wird nicht erzielt. In einem anderen Fall wird das im Programm verwiesene Drittanbieter-Modul auch basierend auf dem Verzeichnis durchsucht, in dem der aktuelle Prozess gestartet wird.
// Beispielprozess.Chdir ('/user/May/documents/test/') // Die aktuelle Prozessverzeichniskonsole.log (process.cwd ());