這篇文章主要介紹“node中進(jìn)程通信的實(shí)現(xiàn)方式有哪些”,在日常操作中,相信很多人在node中進(jìn)程通信的實(shí)現(xiàn)方式有哪些問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”node中進(jìn)程通信的實(shí)現(xiàn)方式有哪些”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供建鄴網(wǎng)站建設(shè)、建鄴做網(wǎng)站、建鄴網(wǎng)站設(shè)計(jì)、建鄴網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、建鄴企業(yè)網(wǎng)站模板建站服務(wù),10多年建鄴做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
通信其實(shí)涵蓋開(kāi)發(fā)的各個(gè)層面,常見(jiàn)的有客戶端和服務(wù)端通過(guò)各種通信協(xié)議進(jìn)行通信,RPC通信,開(kāi)發(fā)過(guò)程中各個(gè)模塊之間的相互通信,electron主進(jìn)程和渲染進(jìn)程之間的通信等等;
本文主要是嘗試總結(jié)下nodejs(單線程,多線程,多進(jìn)程)通信的方式,使用場(chǎng)景,實(shí)現(xiàn)等。
一般進(jìn)程通信的實(shí)現(xiàn)方式如下:
1、Shared Memory(內(nèi)存共享);
2、Socket(套接字);
3、管道(非命名管道Pipe, 命名管道FIFO);
4、Signal(信號(hào));
5、Message queue(消息隊(duì)列);
下面我們看下在node中如何實(shí)現(xiàn)這些方式的通信
單機(jī)下(客戶端內(nèi)單線程,單進(jìn)程里多線程,單臺(tái)服務(wù)器內(nèi)多進(jìn)程),通過(guò)內(nèi)存共享實(shí)現(xiàn)通信的方式最為常見(jiàn)。
從操作系統(tǒng)層面來(lái)看,進(jìn)程內(nèi)所有線程內(nèi)存都是共享的,但前提是需要知道內(nèi)存的訪問(wèn)地址。
但從語(yǔ)言層面(node或者說(shuō)是v8的實(shí)現(xiàn)層面),我們沒(méi)有直接接觸內(nèi)存的管理,而是間接從v8提供的語(yǔ)法/api進(jìn)行內(nèi)存操作。v8提供三種方式給我們共享內(nèi)存(也許叫共享變量更恰當(dāng)):全局變量, 局部變量, 共享傳參(call by sharing);
v8在執(zhí)行代碼之前會(huì)先將代碼通過(guò)Estree規(guī)范轉(zhuǎn)化為抽象語(yǔ)法樹(shù)后再進(jìn)行解釋編譯執(zhí)行,在抽象語(yǔ)法樹(shù)中(關(guān)于抽象語(yǔ)法樹(shù)可查看我另外一篇文章)是有scope的,而內(nèi)存讀取是通過(guò)標(biāo)志符(變量命名)逐級(jí)往上回溯查找。所以如果你需要在兩個(gè)方法之間共享某個(gè)內(nèi)存,可以在他們共同的作用域下進(jìn)行創(chuàng)建。
在客戶端環(huán)境或node環(huán)境,我們都可以實(shí)現(xiàn)多線程,兩者方式也類似(node通過(guò)worker_threads實(shí)現(xiàn),瀏覽器通過(guò)Worker實(shí)現(xiàn))。這里的內(nèi)存共享主要是借助內(nèi)存操作的api(SharedArrayBuffer)實(shí)現(xiàn)的。先看下瀏覽器實(shí)現(xiàn)的例子:
// 主線程 const buffer = new SharedArrayBuffer(1024) const typedArr = new Int16Array(buffer) const newWorker = new Worker('./worker.js') typedArr[0] = 20 newWorker.postMessage(buffer) newWorker.onmessage= (data) => { console.group('[the main thread]'); console.log('Data received from the main thread: %i', typedArr[0]); console.groupEnd(); } // 子線程 addEventListener('message', ({ data }) => { const arr = new Int16Array(data) console.group('[the worker thread]') console.log('Data received from the main thread: %i', arr[0]) console.groupEnd() arr[0] = 18 postMessage('Updated') }) // 結(jié)果 [the worker thread] Data received from the main thread: 20 [the main thread] Data received from the main thread: 18
因?yàn)檫M(jìn)程啟動(dòng)后內(nèi)存是無(wú)法相互讀取的(系統(tǒng)層面的限制),進(jìn)程之間的內(nèi)存共享實(shí)際是通過(guò)新開(kāi)辟一段shared memory實(shí)現(xiàn)的。但node暫時(shí)沒(méi)有支持shared memory,只能通過(guò)低級(jí)語(yǔ)言來(lái)實(shí)現(xiàn),例如: c++實(shí)現(xiàn)的 shared-memory-disruptor addon插件(另外文章介紹)。
Socket 分兩種實(shí)現(xiàn):
1、TCP Socket;
2、UNIX Domain Socket;
兩者的主要區(qū)別如下:
TCP Socket適用于單機(jī),C/S架構(gòu)等.但UNIX Domain Socket只適用于單機(jī)。 UNIX Domain Socket不需要經(jīng)過(guò)一系列的網(wǎng)絡(luò)中轉(zhuǎn)(協(xié)議,分包,校驗(yàn)等等),性能更高,穩(wěn)定性更好。
概念: TCP Socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間抽象層,是一種操作系統(tǒng)提供的進(jìn)程間通信機(jī)制;
TCP Socket通信應(yīng)該是我們?nèi)粘i_(kāi)發(fā)(C/S架構(gòu))中最常見(jiàn)的通信方式之一,在我們?nèi)粘i_(kāi)發(fā)中最常見(jiàn)的就是各種應(yīng)用層協(xié)議(http,websocket,rpc,ftp等)的使用,node中http模塊也是基于net模塊實(shí)現(xiàn)的。
注:其實(shí)UDP也屬于TCP分層(并不是嚴(yán)格的指TCP通信,而是網(wǎng)絡(luò)通信層中的TCP/IP層),node有提供'dgram'模塊來(lái)實(shí)現(xiàn),但在實(shí)際應(yīng)用中沒(méi)有接觸過(guò),所以不進(jìn)行了解。
在node中,TCP Socket是由net模塊實(shí)現(xiàn)的,net模塊主要提供了以下功能:
1、上層的IPC支持(實(shí)際上是管道通信的實(shí)現(xiàn),后面管道通信再詳細(xì)說(shuō)明);
2、net.Server類;
// 服務(wù)端通過(guò)net.createServer創(chuàng)建服務(wù),會(huì)返回net.Server對(duì)象,可以通過(guò)返回值進(jìn)行各種事件監(jiān)聽(tīng),端口監(jiān)聽(tīng) const net = require('net') net.createServer((server => { server.end(`hello world!\n`) })).listen(3302, () => { console.log(`running ...`) })
3、net.Socket類;
const net = require('net') const socket = net.createConnection({port: 3302}) socket.on('data', data => { console.log(data.toString()) })
UNIX Domain Socket是通過(guò)創(chuàng)建一個(gè)文件描述符,不同進(jìn)程之間的通信通過(guò)讀寫(xiě)這個(gè)文件描述符進(jìn)行通信(可以分為創(chuàng)建進(jìn)程和其他進(jìn)程,其他進(jìn)程之間的相互通信可以通過(guò)創(chuàng)建進(jìn)程作為中轉(zhuǎn))。e.g.
// 創(chuàng)建進(jìn)程 const net = require('net') const unixSocketServer = net.createServer(server => { server.on('data', data => { console.log(`receive data: ${data}`) }) }) unixSocketServer.listen('/tmp/test', () => { console.log('listening...') }) // 其他進(jìn)程 const net = require('net') const socket = net.createConnection({path: '/tmp/test'}) socket.on('data', data => { console.log(data.toString()) }) socket.write('my name is vb') // 輸出結(jié)果 listening... receive data: my name is vb
管道通信分兩種,非命名管道和命名管道。
非命名管道的實(shí)現(xiàn)方式和UNIX Domain Socket一樣,都是通過(guò)創(chuàng)建文件描述符進(jìn)行通信。
命名管道是通過(guò)固定的文件描述符進(jìn)行通信:
"\\\\.\\pipe\\" + PIPE_NAME;
源碼可參考stackoverflow(https://stackoverflow.com/questions/11750041/how-to-create-a-named-pipe-in-node-js)
目前理解的管道通信和UNIX Domain Socket實(shí)現(xiàn)基本一致,只是管道通信規(guī)范了讀寫(xiě)權(quán)限,半雙工通信,UNIX Domain Socket更加自由一些。
Signal是操作系統(tǒng)在終止進(jìn)程前給進(jìn)程發(fā)送的信號(hào)。在node中可以通過(guò)process.kill(pid, signal)/child_process.kill(pid, signal)接口實(shí)現(xiàn),e.g.
// 要被終止的http守護(hù)進(jìn)程 const Koa = require('koa') const app = new Koa() app.listen(3004, () => { console.log(`process pid is : ${process.pid}`) // process pid is : 75208 }) // 操作進(jìn)程 process.kill(75208, 'SIGHUP') // 'SIGHUP'是一般結(jié)束進(jìn)程的信號(hào),還有更多其他的信號(hào)參考 [標(biāo)識(shí)](https://blog.csdn.net/houjixin/article/details/71430489)
但這里的前提是你需要獲取到被終止的進(jìn)程pid,更多pid的內(nèi)容可閱讀我之前關(guān)于進(jìn)程的文章。
一開(kāi)始我以為是redis,各種MQ之類的基于TCP的消息隊(duì)列。但其實(shí)是操作系統(tǒng)內(nèi)的消息隊(duì)列,node暫時(shí)沒(méi)有提供相關(guān)的上層接口,需要更底層實(shí)現(xiàn),e.g. svmq
到此,關(guān)于“node中進(jìn)程通信的實(shí)現(xiàn)方式有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!