Nodejs每日一講之Nodejs的進程間通信
本文轉(zhuǎn)載自微信公眾號「編程雜技 」,作者theanarkh。轉(zhuǎn)載本文請聯(lián)系編程雜技 眾號。
之前提了一個問題:nodejs中如何實現(xiàn)兄弟進程間的通信,大家分別列舉了redis、ZooKeeper,MessageChannel,還有l(wèi)inux操作系統(tǒng)提供的共享內(nèi)存等一系列的進程間通信方式。所以今天來分享一下到底如何實現(xiàn)nodejs的進程間通信。這里的討論只限于linux系統(tǒng),本機的進程。情況分為兩種:父子進程,兄弟進程。
在nodejs中,實現(xiàn)進程間通信的方式其實只有一種,那就是unix域。linux系統(tǒng)提供了很多種進程間通信的方式,那么為什么nodejs選擇unix域的,因為unix域相比其他進程間通信方式,有一個獨特的優(yōu)勢,那就是傳遞文件描述符。unix域的實現(xiàn)是基于c/s模式的,類似tcp,udp。首先需要啟動一個unix域服務(wù)器,然后各個unix客戶端就可以"連接"這個服務(wù)器進行通信。
而在nodejs中父子進程的通信,底層使用的是socketpair,socketpair底層是也是unix域,不過他不是基于c/s模式的,如下圖所示。
那么nodejs中,兄弟進程是如何通信的呢?最簡單的方式就是通過主進程
但是這里多了一次中轉(zhuǎn),很明顯效率上面會存著一些問題(相對直達和多一次中轉(zhuǎn),性能比較是很明顯的,但是沒具體測過。而且據(jù)說egg就是這么搞的,有了解的同學(xué)可以交流一下)。所以我們一般通過unix域?qū)崿F(xiàn)兄弟進程的通信,但是我們需要做的事情就比較多了。我們看看unix域的類型。Unix域支持兩種數(shù)據(jù)模式
1 流式( SOCK_STREAM),類似tcp,數(shù)據(jù)為字節(jié)流,需要應(yīng)用層處理粘包問題。
2 數(shù)據(jù)報模式( SOCK_DGRAM ),類似udp,不需要處理數(shù)據(jù)邊界。
但是不巧的是Nodejs使用的是流式模式,所以問題就變得復(fù)雜。這時候我們通過c/s模式雖然可以實現(xiàn)兄弟進程間的通信,但是我們拿到的數(shù)據(jù)可能是"亂的",這時候為什么呢?一般情況下,客戶端給服務(wù)器發(fā)送1個字節(jié),然后服務(wù)器處理,如果是基于這種場景,那么數(shù)據(jù)就不會是亂的。因為每次就是一個需要處理的數(shù)據(jù)單位。但是如果客戶端給服務(wù)器發(fā)送1個字節(jié),服務(wù)器還沒來得及處理,客戶端又發(fā)送了一個字節(jié),那么這時候服務(wù)器再處理的時候,就會有問題。因為兩個字節(jié)混一起了。就好比在一個tcp連接上先后發(fā)送兩個http請求一樣,如果服務(wù)器沒有辦法判斷兩個請求的數(shù)據(jù)邊界,那么處理就會有問題。
我們寫一個測試的例子。
unix域服務(wù)器
const net = require('net');net.createServer((client) => { client.on('data', (data) => { console.log(data.toString('utf-8')) })}).listen('\\\\?\\pipe\\ipc')
unix域客戶端
const net = require('net');const socket = net.connect({path: '\\\\?\\pipe\\ipc'});setInterval(() => { socket.write('1'); socket.write('2');},1000)
我們看一下輸出
我們看到輸出可能是1然后2。也可能是12。在tcp協(xié)議中,這叫做粘包。那么我們?nèi)绾谓鉀Q這個問題呢?我們可以定義一個應(yīng)用層協(xié)議。類似http協(xié)議一樣,有了協(xié)議我們就知道,如何去解析收到的數(shù)據(jù)。接著我們還需要實現(xiàn)這個協(xié)議的解析器和封包邏輯,做完這些,我們就可以實現(xiàn)兄弟進程的通信了。
具體可參考ipc庫的實現(xiàn)https://github.com/theanarkh/nodejs-ipc