Heim > Artikel > Web-Frontend > Erfahren Sie mehr darüber, wie Node.js und Electron zwischen Prozessen kommunizieren
In diesem Artikel wird das Prinzip der Prozesskommunikation zwischen Node.js und Electron untersucht, vorgestellt, wie Electron die Kommunikation verarbeitet, wie der child_process und der Cluster von nodejs die Kommunikation verarbeiten und das Wesen der Prozesskommunikation verstehen.
Warum das Front-End Prozesskommunikation verstehen muss:
Im Front-End-Bereich geht es nicht mehr nur um das Schreiben von Seiten, die im Browser ausgeführt werden, sondern auch um Electron, NodeJS usw., und beide Technologien müssen beherrscht werden Prozesskommunikation.
nodejs ist eine Laufzeit von js. Es erweitert viele APIs, die Betriebssystemfunktionen kapseln, einschließlich prozess- und threadbezogener APIs. Das Erlernen von Prozess-APIs erfordert das Erlernen des Kommunikationsmechanismus zwischen Prozessen.
Elektron ist eine Desktop-Entwicklungslösung, die auf Chrom und NodeJS basiert. Die Kommunikation zwischen diesen beiden Prozessen ist ebenfalls erforderlich. [Empfohlene Studie: „nodejs Tutorial“]
In diesem Artikel werfen wir einen tieferen Blick auf die Prozesskommunikation.
In diesem Artikel werden die folgenden Wissenspunkte erläutert:
Ein Prozess ist exklusiv für die angeforderten Ressourcen. Jeder Prozess kann nur auf seine eigenen Ressourcen zugreifen.
ProzesskommunikationSemaphore
Wenn es sich um eine einfache Markierung handelt, die durch eine Zahl dargestellt und in einem Attribut der Leiterplatte platziert wird, wird dies als Semaphore
bezeichnet. Beispielsweise kann die Implementierung einer Sperre erfolgen durch eine Signalgröße umgesetzt werden. Diese Semaphor-Idee wird auch häufig beim Schreiben von Front-End-Code verwendet. Beispielsweise muss bei der Implementierung der Drosselung auch eine Markierungsvariable hinzugefügt werden.
信号量
,比如锁的实现就可以通过信号量。
这种信号量的思想我们写前端代码也经常用,比如实现节流的时候,也要加一个标记变量。
管道
但是信号量不能传递具体的数据啊,传递具体数据还得用别的方式。比如我们可以通过读写文件的方式来通信,这就是管道
,如果是在内存中的文件,叫做匿名管道,没有文件名,如果是真实的硬盘的文件,是有文件名的,叫做命名管道。
文件需要先打开,然后再读和写,之后再关闭,这也是管道的特点。管道是基于文件的思想封装的,之所以叫管道,是因为只能一个进程读、一个进程写,是单向的(半双工)。而且还需要目标进程同步的消费数据,不然就会阻塞住。
这种管道的方式实现起来很简单,就是一个文件读写,但是只能用在两个进程之间通信,只能同步的通信。其实管道的同步通信也挺常见的,就是 stream 的 pipe 方法。
消息队列
管道实现简单,但是同步的通信比较受限制,那如果想做成异步通信呢?加个队列做缓冲(buffer)不就行了,这就是消息队列
Aber die Semaphore kann keine bestimmten Daten übertragen. Sie müssen andere Methoden verwenden, um bestimmte Daten zu übertragen. Wir können beispielsweise durch Lesen und Schreiben von Dateien kommunizieren. Wenn es sich um eine Datei im Speicher handelt, wird sie als anonyme Pipe bezeichnet und hat keinen Dateinamen Auf einer echten Festplatte gibt es eine Datei, die als Named Pipe bezeichnet wird. Die Datei muss zuerst geöffnet, dann gelesen und geschrieben und dann wieder geschlossen werden. Dies ist auch eine Eigenschaft von Pipelines. Pipes werden basierend auf der Idee von Dateien gekapselt. Sie werden Pipes genannt, weil sie nur von einem Prozess gelesen und von einem Prozess geschrieben werden können. Sie sind unidirektional (Halbduplex). Darüber hinaus muss der Zielprozess auch Daten synchron verbrauchen, sonst wird er blockiert.
Diese Pipeline-Methode ist sehr einfach zu implementieren. Es handelt sich um ein Lesen und Schreiben von Dateien, kann jedoch nur für die Kommunikation zwischen zwei Prozessen verwendet werden und kann nur synchron kommunizieren. Tatsächlich ist auch die synchrone Kommunikation von Pipes weit verbreitet, bei der es sich um die Pipe-Methode des Streams handelt. 🎜🎜🎜🎜Message Queue🎜🎜🎜🎜Die Pipeline-Implementierung ist einfach, aber die synchrone Kommunikation ist relativ begrenzt. Was ist, wenn Sie eine asynchrone Kommunikation durchführen möchten? Fügen Sie einfach eine Warteschlange als Puffer hinzu. Dies istNachrichtenwarteschlange
. 🎜🎜Die Nachrichtenwarteschlange ist ebenfalls eine Kommunikation zwischen zwei Prozessen, basiert jedoch nicht auf der dateibasierten Idee. Obwohl sie ebenfalls einseitig ist, weist sie einen gewissen asynchronen Charakter auf und kann viele Nachrichten gleichzeitig ablegen und konsumieren. 🎜🎜🎜🎜Gemeinsamer Speicher🎜🎜🎜Pipelines und Nachrichtenwarteschlangen liegen zwischen zwei Prozessen. Was ist, wenn es mehrere Prozesse gibt?
Wir können auf diese Weise kommunizieren, indem wir ein Stück Speicher beantragen, das von mehreren Prozessen betrieben werden kann, genannt 共享内存
. Jeder Prozess kann Daten in diesen Speicher lesen und schreiben, was relativ effizient ist.
Obwohl Shared Memory effizient ist und für die Kommunikation zwischen mehreren Prozessen verwendet werden kann, ist es nicht gut, da mehrere Prozesse lesen und schreiben können, sodass es leicht zu Verwirrung kommt. Sie müssen die Reihenfolge selbst steuern, z Semaphor des zu steuernden Prozesses.
Shared Memory eignet sich für die Kommunikation zwischen mehreren Prozessen und erfordert kein Zwischenmedium, ist also effizienter, aber auch komplizierter in der Nutzung.
Die oben genannten Möglichkeiten sind fast alle für die lokale Prozesskommunikation. Warum eine lokale hinzufügen?
Prozesskommunikation ist ipc (Inter-Process Communication). Die beiden Prozesse können von einem Computer stammen oder es können Prozesse verschiedener Computer im Netzwerk sein, daher gibt es zwei Prozessmethoden Kommunikation:
Lokaler Prozeduraufruf LPC (lokaler Prozeduraufruf), Remoteprozeduraufruf RPC (Remoteprozeduraufruf).
Lokale Prozeduraufrufe sind die oben erwähnten Kommunikationsmethoden von Semaphoren, Pipes, Nachrichtenwarteschlangen und gemeinsam genutztem Speicher. Wenn sie sich jedoch im Netzwerk befinden, müssen wir sie tatsächlich häufiger verwenden, z http, Websocket.
Wenn also jemand IPC erwähnt, spricht er von Prozesskommunikation, die in zwei Arten unterteilt werden kann: lokal und remote.
Remote-Protokolle werden auf der Grundlage von Netzwerkprotokollen gekapselt, während lokale Protokolle auf der Grundlage von Semaphoren, Pipes, Nachrichtenwarteschlangen und gemeinsam genutztem Speicher wie Electron und NodeJS gekapselt werden, die wir als Nächstes besprechen werden.
Elektronen starten zunächst den Hauptprozess, erstellen dann einen Rendering-Prozess über BrowserWindow und laden die HTML-Seite zum Rendern. Die Kommunikation zwischen den beiden Prozessen erfolgt über die von Electron bereitgestellte IPC-API.
ipcMain, ipcRenderer
Im Hauptprozess werden Ereignisse durch die On-Methode von ipcMain überwacht.
import { ipcMain } from 'electron'; ipcMain.on('异步事件', (event, arg) => { event.sender.send('异步事件返回', 'yyy'); })
Im Rendering-Prozess werden Ereignisse durch die On-Methode von ipcRenderer überwacht und Nachrichten werden durch Send gesendet
import { ipcRenderer } from 'electron'; ipcRender.on('异步事件返回', function (event, arg) { const message = `异步消息: ${arg}` }) ipcRenderer.send('异步事件', 'xxx')api ist relativ einfach zu verwenden. Dies ist der Prozess. Die C++-Schicht wird gekapselt und dann der Ereignisform-API von JS ausgesetzt. Wir können darüber nachdenken, auf welchem Mechanismus es basiert? Offensichtlich gibt es einen gewissen Grad an asynchroner Natur, und es handelt sich um eine Kommunikation zwischen übergeordneten und untergeordneten Prozessen, sodass sie in Form einer Nachrichtenwarteschlange implementiert wird.
remote
Neben ereignisbasierten APIs bietet Electron auch Remote-Methodenaufruf-RMI-APIs (Remote Method Invoke) an. Tatsächlich handelt es sich um eine weitere Kapselung der Nachricht, das heißt, das Aufrufen verschiedener Methoden entsprechend der übergebenen Nachricht ähnelt in der Form dem Aufruf der Methode dieses Prozesses, erfolgt jedoch tatsächlich durch Senden der Nachricht zu einem anderen Prozess und ipcMain, ipcRenderer Die Form ist im Wesentlichen dieselbe. Verwenden Sie beispielsweise im Renderprozess Remote, um die BrowserWindow-API direkt aufzurufen, die nur im Hauptprozess verfügbar ist.const { BrowserWindow } = require('electron').remote; let win = new BrowserWindow({ width: 800, height: 600 }); win.loadURL('https://github.com');Zusammenfassend lässt sich sagen, dass die Eltern-Kind-Prozesskommunikationsmethode auf der Kapselung von Nachrichten basiert. Eine davon ist die Ereignismethode, die über die API von ipcMain und ipcRenderer verwendet wird Der Methodenaufruf (rmi) basiert ebenfalls auf Nachrichten am unteren Rand. Er führt Remote-Methoden aus, sieht jedoch so aus, als würden lokale Methoden ausgeführt.
child_process
child_process stellt Spawn-, Exec-, ExecFile- und Fork-APIs bereit, mit denen verschiedene Prozesse erstellt werden:spawn, exec
Wenn Sie Befehle über die Shell ausführen möchten, verwenden Sie spawn oder exec. Da die Ausführung von Befehlen im Allgemeinen einen Rückgabewert erfordert, unterscheiden sich die beiden APIs in der Art und Weise, wie sie Werte zurückgeben. spawn gibt einen Stream zurück, der über das Datenereignis abgerufen wird, und exec wird weiter in Puffer unterteilt, was einfacher zu verwenden ist, aber maxBuffer überschreiten kann.const { spawn } = require('child_process'); var app = spawn('node','main.js' {env:{}}); app.stderr.on('data',function(data) { console.log('Error:',data); }); app.stdout.on('data',function(data) { console.log(data); });Tatsächlich ist exec basierend auf spwan gekapselt und kann in einfachen Szenarien verwendet werden. Manchmal muss maxBuffer festgelegt werden.
const { exec } = require('child_process'); exec('find . -type f', { maxBuffer: 1024*1024 }(err, stdout, stderr) => { if (err) { console.error(`exec error: ${err}`); return; } console.log(stdout); });
execFile
Wenn Sie zusätzlich zum Ausführen von Befehlen eine ausführbare Datei ausführen möchten, verwenden Sie die execFile-API:const { execFile } = require('child_process'); const child = execFile('node', ['--version'], (error, stdout, stderr) => { if (error) { throw error; } console.log(stdout); });
fork
Und wenn Sie js ausführen möchten, verwenden Sie fork:const { fork } = require('child_process'); const xxxProcess = fork('./xxx.js'); xxxProcess.send('111111'); xxxProcess.on('message', sum => { res.end('22222'); });
Zusammenfassung
Eine kurze Zusammenfassung der vier APIs von child_process:Wenn Sie Shell-Befehle ausführen möchten, verwenden Sie spawn und exec, um einen Stream zurückzugeben, und exec wird weiter in einen Puffer gekapselt. Abgesehen davon, dass exec manchmal maxBuffer festlegen muss, gibt es keinen Unterschied. Wenn Sie eine ausführbare Datei ausführen möchten, verwenden Sie execFile. Wenn Sie die js-Datei ausführen möchten, verwenden Sie fork.child_process 的进程通信
说完了 api 我们来说下 child_process 创建的子进程怎么和父进程通信,也就是怎么做 ipc。
pipe
首先,支持了 pipe,很明显是通过管道的机制封装出来的,能同步的传输流的数据。
const { spawn } = require('child_process'); const find = spawn('cat', ['./aaa.js']); const wc = spawn('wc', ['-l']); find.stdout.pipe(wc.stdin);
比如上面通过管道把一个进程的输出流传输到了另一个进程的输入流,和下面的 shell 命令效果一样:
cat ./aaa.js | wc -l
message
spawn 支持 stdio 参数,可以设置和父进程的 stdin、stdout、stderr 的关系,比如指定 pipe 或者 null。还有第四个参数,可以设置 ipc,这时候就是通过事件的方式传递消息了,很明显,是基于消息队列实现的。
const { spawn } = require('child_process'); const child = spawn('node', ['./child.js'], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] }); child.on('message', (m) => { console.log(m); }); child.send('xxxx');
而 fork 的 api 创建的子进程自带了 ipc 的传递消息机制,可以直接用。
const { fork } = require('child_process'); const xxxProcess = fork('./xxx.js'); xxxProcess.send('111111'); xxxProcess.on('message', sum => { res.end('22222'); });
cluster
cluster 不再是父子进程了,而是更多进程,也提供了 fork 的 api。
比如 http server 会根据 cpu 数启动多个进程来处理请求。
import cluster from 'cluster'; import http from 'http'; import { cpus } from 'os'; import process from 'process'; const numCPUs = cpus().length; if (cluster.isPrimary) { for (let i = 0; i < numCPUs; i++) { cluster.fork(); } } else { const server = http.createServer((req, res) => { res.writeHead(200); res.end('hello world\n'); }) server.listen(8000); process.on('message', (msg) => { if (msg === 'shutdown') { server.close(); } }); }
它同样支持了事件形式的 api,用于多个进程之间的消息传递,因为多个进程其实也只是多个父子进程的通信,子进程之间不能直接通信,所以还是基于消息队列实现的。
共享内存
子进程之间通信还得通过父进程中转一次,要多次读写消息队列,效率太低了,就不能直接共享内存么?
现在 nodejs 还是不支持的,可以通过第三方的包 shm-typed-array 来实现,感兴趣可以看一下。
https://www.npmjs.com/package/shm-typed-array
进程包括代码、数据和 PCB,是程序的一次执行的过程,PCB 记录着各种执行过程中的信息,比如分配的资源、执行到的地址、用于通信的数据结构等。
进程之间需要通信,可以通过信号量、管道、消息队列、共享内存的方式。
信号量就是一个简单的数字的标记,不能传递具体数据。
管道是基于文件的思想,一个进程写另一个进程读,是同步的,适用于两个进程。
消息队列有一定的 buffer,可以异步处理消息,适用于两个进程。
共享内存是多个进程直接操作同一段内存,适用于多个进程,但是需要控制访问顺序。
这四种是本地进程的通信方式,而网络进程则基于网络协议的方式也可以做进程通信。
进程通信叫做 ipc,本地的叫做 lpc,远程的叫 rpc。
其中,如果把消息再封装一层成具体的方法调用,叫做 rmi,效果就像在本进程执行执行另一个进程的方法一样。
electron 和 nodejs 都是基于上面的操作系统机制的封装:
elctron 支持 ipcMain 和 ipcRenderer 的消息传递的方式,还支持了 remote 的 rmi 的方式。
nodejs 有 child_process 和 cluster 两个模块和进程有关,child_process 是父子进程之间,cluster 是多个进程:
child_process 提供了用于执行 shell 命令的 spawn、exec,用于执行可执行文件的 execFile,用于执行 js 的 fork。提供了 pipe 和 message 两种 ipc 方式。
cluster 也提供了 fork,提供了 message 的方式的通信。
当然,不管封装形式是什么,都离不开操作系统提供的信号量、管道、消息队列、共享内存这四种机制。
ipc 是开发中频繁遇到的需求,希望这篇文章能够帮大家梳理清楚从操作系统层到不同语言和运行时的封装层次的脉络。
原文地址:https://juejin.cn/post/6988484297485189127
作者:zxg_神说要有光
更多编程相关知识,请访问:编程视频!!
Das obige ist der detaillierte Inhalt vonErfahren Sie mehr darüber, wie Node.js und Electron zwischen Prozessen kommunizieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!