node 利用進程通信實現(xiàn)Cluster共享內(nèi)存
Node.js的標準API沒有提供進程共享內(nèi)存,然而通過IPC接口的send方法和對message事件的監(jiān)聽,就可以實現(xiàn)一個多進程之間的協(xié)同機制,通過通信來操作共享內(nèi)存。
##IPC的基本用法:
// worker進程 發(fā)送消息
process.send(‘讀取共享內(nèi)存');
// master進程 接收消息 -> 處理 -> 發(fā)送回信
cluster.on('online', function (worker) {
// 有worker進程建立,即開始監(jiān)聽message事件
worker.on(‘message', function(data) {
// 處理來自worker的請求
// 回傳結(jié)果
worker.send(‘result')
});
});
在Node.js中,通過send和on(‘message', callback)實現(xiàn)的IPC通信有幾個特點。首先,master和worker之間可以互相通信,而各個worker之間不能直接通信,但是worker之間可以通過master轉(zhuǎn)發(fā)實現(xiàn)間接通信。另外,通過send方法傳遞的數(shù)據(jù),會先被JSON.stringify處理后再傳遞,接收后會再用JSON.parse解析。所以Buffer對象傳遞后會變成數(shù)組,而function則無法直接傳遞。反過來說,就是可以直接傳遞除了buffer和function之外的所有數(shù)據(jù)類型(已經(jīng)很強大了,而且buffer和function也可以用變通的方法實現(xiàn)傳遞)。
基于以上特點,我們可以設(shè)計一個通過IPC來共享內(nèi)存的方案:
1、worker進程作為共享內(nèi)存的使用者,并不直接操作共享內(nèi)存,而是通過send方法通知master進程進行寫入(set)或者讀取(get)操作。
2、master進程初始化一個Object對象作為共享內(nèi)存,并根據(jù)worker發(fā)來的message,對Object的鍵值進行讀寫。
3、由于要使用跨進程通信,所以worker發(fā)起的set和get都是異步操作,master根據(jù)請求進行實際讀寫操作,然后將結(jié)果返回給worker(即把結(jié)果數(shù)據(jù)send給worker)。
##數(shù)據(jù)格式
為了實現(xiàn)進程間異步的讀寫功能,需要對通信數(shù)據(jù)的格式做一點規(guī)范。
首先是worker的請求數(shù)據(jù):
requestMessage = {
isSharedMemoryMessage: true, // 表示這是一次共享內(nèi)存的操作通信
method: ‘set', // or ‘get' 操作的方法
id: cluster.worker.id, // 發(fā)起操作的進程(在一些特殊場景下,用于保證master可以回信)
uuid: uuid, // 此次操作的(用于注冊/調(diào)用回調(diào)函數(shù))
key: key, // 要操作的鍵
value: value // 鍵對應(yīng)的值(寫入)
}
master在接到數(shù)據(jù)后,會根據(jù)method執(zhí)行相應(yīng)操作,然后根據(jù)requestMessage.id將結(jié)果數(shù)據(jù)發(fā)給對應(yīng)的worker,數(shù)據(jù)格式如下:
responseMessage = {
isSharedMemoryMessage: true, // 標記這是一次共享內(nèi)存通信
uuid: requestMessage.uuid, // 此次操作的唯一標示
value: value // 返回值。get操作為key對應(yīng)的值,set操作為成功或失敗
}
規(guī)范數(shù)據(jù)格式的意義在于,master在接收到請求后,能夠?qū)⑻幚斫Y(jié)果發(fā)送給對應(yīng)的worker,而worker在接到回傳的結(jié)果后,能夠調(diào)用此次通信對應(yīng)的callback,從而實現(xiàn)協(xié)同。
規(guī)范數(shù)據(jù)格式后,接下來要做的就是設(shè)計兩套代碼,分別用于master進程和worker進程,監(jiān)聽通信并處理通信數(shù)據(jù),實現(xiàn)共享內(nèi)存的功能。
##User類
User類的實例在worker進程中工作,負責發(fā)送操作共享內(nèi)存的請求,并監(jiān)聽master的回信。
var User = function() {
var self = this;
self.__uuid__ = 0;
// 緩存回調(diào)函數(shù)
self.__getCallbacks__ = {};
// 接收每次操作請求的回信
process.on('message', function(data) {
if (!data.isSharedMemoryMessage) return;
// 通過uuid找到相應(yīng)的回調(diào)函數(shù)
var cb = self.__getCallbacks__[data.uuid];
if (cb && typeof cb == 'function') {
cb(data.value)
}
// 卸載回調(diào)函數(shù)
self.__getCallbacks__[data.uuid] = undefined;
});
};
// 處理操作
User.prototype.handle = function(method, key, value, callback) {
var self = this;
var uuid = self.__uuid__++;
process.send({
isSharedMemoryMessage: true,
method: method,
id: cluster.worker.id,
uuid: uuid,
key: key,
value: value
});
// 注冊回調(diào)函數(shù)
self.__getCallbacks__[uuid] = callback;
};
User.prototype.set = function(key, value, callback) {
this.handle('set', key, value, callback);
};
User.prototype.get = function(key, callback) {
this.handle('get', key, null, callback);
};
##Manager類
Manager類的實例在master進程中工作,用于初始化一個Object作為共享內(nèi)存,并根據(jù)User實例的請求,在共享內(nèi)存中增加鍵值對,或者讀取鍵值,然后將結(jié)果發(fā)送回去。
var Manager = function() {
var self = this;
// 初始化共享內(nèi)存
self.__sharedMemory__ = {};
// 監(jiān)聽并處理來自worker的請求
cluster.on('online', function(worker) {
worker.on('message', function(data) {
// isSharedMemoryMessage是操作共享內(nèi)存的通信標記
if (!data.isSharedMemoryMessage) return;
self.handle(data);
});
});
};
Manager.prototype.handle = function(data) {
var self = this;
var value = this[data.method](data);
var msg = {
// 標記這是一次共享內(nèi)存通信
isSharedMemoryMessage: true,
// 此次操作的唯一標示
uuid: data.uuid,
// 返回值
value: value
};
cluster.workers[data.id].send(msg);
};
// set操作返回ok表示成功
Manager.prototype.set = function(data) {
this.__sharedMemory__[data.key] = data.value;
return 'OK';
};
// get操作返回key對應(yīng)的值
Manager.prototype.get = function(data) {
return this.__sharedMemory__[data.key];
};
##使用方法
if (cluster.isMaster) {
// 初始化Manager的實例
var sharedMemoryManager = new Manager();
// fork第一個worker
cluster.fork();
// 1秒后fork第二個worker
setTimeout(function() {
cluster.fork();
}, 1000);
} else {
// 初始化User類的實例
var sharedMemoryUser = new User();
if (cluster.worker.id == 1) {
// 第一個worker向共享內(nèi)存寫入一組數(shù)據(jù),用a標記
sharedMemoryUser.set('a', [0, 1, 2, 3]);
}
if (cluster.worker.id == 2) {
// 第二個worker從共享內(nèi)存讀取a的值
sharedMemoryUser.get('a', function(data) {
console.log(data); // => [0, 1, 2, 3]
});
}
}
以上就是一個通過IPC通信實現(xiàn)的多進程共享內(nèi)存功能,需要注意的是,這種方法是直接在master進程的內(nèi)存里緩存數(shù)據(jù),必須注意內(nèi)存的使用情況,這里可以考慮加入一些簡單的淘汰策略,優(yōu)化內(nèi)存的使用。另外,如果單次讀寫的數(shù)據(jù)比較大,IPC通信的耗時也會相應(yīng)增加。
完整代碼:https://github.com/x6doooo/sharedmemory
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Mongoose經(jīng)常返回e11000 error的原因分析
這篇文章主要給大家分析了Mongoose經(jīng)常返回e11000 error的原因,文中介紹的非常詳細,對大家具有一定的參考價值,需要的朋友可以們下面來一起看看吧。2017-03-03
npm安裝的全局包/工具不能使用,不是內(nèi)部/外部命令的解決方法
這篇文章主要給大家介紹了關(guān)于npm安裝的全局包/工具不能使用,不是內(nèi)部/外部命令的解決方法,文中通過圖文將解決的方法介紹的非常詳細,需要的朋友可以參考下2023-03-03
node故障定位頂級技巧動態(tài)追蹤Dynamic?Trace詳解
這篇文章主要為大家介紹了node故障定位頂級技巧動態(tài)追蹤Dynamic?Trace詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09
Nodejs實現(xiàn)爬蟲抓取數(shù)據(jù)實例解析
這篇文章主要介紹了Nodejs實現(xiàn)爬蟲抓取數(shù)據(jù)實例解析,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友參考下吧2018-07-07
NodeJS鏈接MySql數(shù)據(jù)庫的操作方法
下面小編就為大家?guī)硪黄狽odeJS鏈接MySql數(shù)據(jù)庫的操現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
nodejs實現(xiàn)發(fā)出蜂鳴聲音(系統(tǒng)報警聲)的方法
這篇文章主要介紹了nodejs實現(xiàn)發(fā)出蜂鳴聲音(系統(tǒng)報警聲)的方法,結(jié)合實例形式分析了nodejs發(fā)出蜂鳴聲的原理及具體應(yīng)用方法,需要的朋友可以參考下2017-01-01
websocket+node.js實現(xiàn)實時聊天系統(tǒng)問題咨詢
最近新學習websocket,做了一個實時聊天。用Node.js搭建的服務(wù):serevr.js. 兩個相互通信頁面:client.html 和server.html 但是就是有很多問題,下面通過本文給大家分享下2017-05-05

