新聞中心
穩(wěn)定性: 2 - 不穩(wěn)定單個node.js實例在單線程中運行,在某些情況下,它可能出現(xiàn)負(fù)載,因此為了能夠更好的利用多核系統(tǒng)的能力,你可以使用Node.js內(nèi)置的集群(cluster)功能來處理負(fù)載。

公司主營業(yè)務(wù):網(wǎng)站建設(shè)、成都做網(wǎng)站、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。成都創(chuàng)新互聯(lián)公司是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)公司推出渝中免費做網(wǎng)站回饋大家。
在集群模塊里很容易就能創(chuàng)建一個共享所有服務(wù)器接口的進(jìn)程。
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
// Workers can share any TCP connection
// In this case its a HTTP server
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
}運行Node后,將會在所有工作進(jìn)程里共享8000端口。
% NODE_DEBUG=cluster node server.js
23521,Master Worker 23524 online
23521,Master Worker 23526 online
23521,Master Worker 23523 online
23521,Master Worker 23528 online這個特性是最近才引入的,大家可以試試并提供反饋。
還要注意,在Windows系統(tǒng)里還不能在工作進(jìn)程中創(chuàng)建一個被命名的管道服務(wù)器。
如何工作
child_process.fork方法派生工作進(jìn)程,所以它能通過IPC和父進(jìn)程通訊,并相互傳遞句柄。
集群模塊通過以下的兩種分發(fā)模式來處理連接:
第一種(默認(rèn)方法,除了Windows平臺)為循環(huán)式。主進(jìn)程監(jiān)聽一個端口,接收新的連接,再輪流的分發(fā)給工作進(jìn)程。
第二種,主進(jìn)程監(jiān)聽socket,并發(fā)送給感興趣的工作進(jìn)程,工作進(jìn)程直接接收連接。
比較上述兩種方法,第二種方法理論上性能最高。實際上,由于操作系統(tǒng)各式各樣,分配往往分配不均。比如,70%的連接終止于2個進(jìn)程,實際上共有8個進(jìn)程。
因為server.listen()將大部分工作交給了主進(jìn)程,所以一個普通的Node.js進(jìn)程和一個集群工作進(jìn)程會在三種情況下有所區(qū)別:
server.listen({fd: 7})由于消息被傳回主進(jìn)程,所以將會監(jiān)聽主進(jìn)程里的文件描述符,而不是其他工作進(jìn)程里的文件描述符 7。server.listen(handle)監(jiān)聽一個明確地句柄,會使得工作進(jìn)程使用指定句柄,而不是與主進(jìn)程通訊。如果工作進(jìn)程已經(jīng)擁有了該句柄,前提是您知道在做什么。server.listen(0)通常它會讓服務(wù)器隨機(jī)監(jiān)聽端口。然而在集群里每個工作進(jìn)程listen(0)時會收到相同的端口。實際上僅第一次是隨機(jī)的,之后是可預(yù)測的。如果你想監(jiān)聽一個特定的端口,可以根據(jù)集群的工作進(jìn)程的ID生產(chǎn)一個端口ID 。
在Node.js或你的程序里沒有路由邏輯,工作進(jìn)程見也沒有共享狀態(tài)。因此,像登錄和會話這樣的工作,不要設(shè)計成過度依賴內(nèi)存里的對象。
因為工作線程都是獨立的,你可以根據(jù)需求來殺死或者派生而不會影響其他進(jìn)程。只要仍然有工作進(jìn)程,服務(wù)器還會接收連接。Node不會自動管理工作進(jìn)程的數(shù)量,這是你的責(zé)任,你可以根據(jù)自己需求來管理。
cluster.schedulingPolicy
調(diào)度策略cluster.SCHED_RR表示輪流制,cluster.SCHED_NONE表示操作系統(tǒng)處理。這是全局性的設(shè)定,一旦你通過cluster.setupMaster()派生了第一個工作進(jìn)程,它就不可更改了。
SCHED_RR是除Windows外所有系統(tǒng)的默認(rèn)設(shè)置。只要libuv能夠有效地分配IOCP句柄并且不產(chǎn)生巨大的性能損失,Windows也會改為SCHED_RR方式。
cluster.schedulingPolicy也可通過環(huán)境變量NODE_CLUSTER_SCHED_POLICY來更改。有效值為"rr"和"none"。
cluster.settings
- {Object}
execArgv{Array} 傳給可執(zhí)行的Node的參數(shù)列表(默認(rèn)=process.execArgv)exec{String} 執(zhí)行文件的路徑。 (默認(rèn)=process.argv[1])args{Array} 傳給工作進(jìn)程的參數(shù)列表(默認(rèn)=process.argv.slice(2))silent{Boolean}是否將輸出發(fā)送給父進(jìn)程的stdio。(默認(rèn)=false)uid{Number} 設(shè)置用戶進(jìn)程的ID。 (參考setuid(2)。)gid{Number} 設(shè)置進(jìn)程組的ID。 (參考setgid(2)。)
調(diào)用.setupMaster()(或.fork())方法后,這個settings對象會包含設(shè)置內(nèi)容,包括默認(rèn)值。
設(shè)置后會立即凍結(jié),因為.setupMaster()只能調(diào)用一次。
這個對象不應(yīng)該被手動改變或設(shè)置。
cluster.isMaster
- {Boolean}
如果是主進(jìn)程,返回true。如果process.env.NODE_UNIQUE_ID未定義,則isMaster為true。
cluster.isWorker
- {Boolean}
如果不是主進(jìn)程返回true(和cluster.isMaster相反)。
事件: 'fork'
worker{Worker object}
當(dāng)一個新的工作進(jìn)程被分支出來,集群模塊會產(chǎn)生'fork'事件。它可用于記錄工作進(jìn)程,并創(chuàng)建自己的超時管理。
var timeouts = [];
function errorMsg() {
console.error("Something must be wrong with the connection ...");
}
cluster.on('fork', function(worker) {
timeouts[worker.id] = setTimeout(errorMsg, 2000);
});
cluster.on('listening', function(worker, address) {
clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', function(worker, code, signal) {
clearTimeout(timeouts[worker.id]);
errorMsg();
});事件: 'online'
worker{Worker object}
分支出一個新的工作進(jìn)程后,它會響應(yīng)在線消息。當(dāng)主線程接收到在線消息后,它會觸發(fā)這個事件。'fork'和'online'之間的區(qū)別在于,主進(jìn)程分支一個工作進(jìn)程后會調(diào)用 fork,而工作進(jìn)程運行后會調(diào)用emitted。
cluster.on('online', function(worker) {
console.log("Yay, the worker responded after it was forked");
});事件: 'listening'
worker{Worker object}address{Object}
工作進(jìn)程調(diào)用listen()時,服務(wù)器會觸發(fā)'listening'事件,同時也會在主進(jìn)程的集群里觸發(fā)。
事件處理函數(shù)有兩個參數(shù),worker包含工作進(jìn)程對象,address包含以下屬性:address, port和addressType。如果工作進(jìn)程監(jiān)聽多個地址的時候,這些東西非常有用。
cluster.on('listening', function(worker, address) {
console.log("A worker is now connected to " + address.address + ":" + address.port);
});addressType是以下內(nèi)容:
4(TCPv4)6(TCPv6)-1(unix domain socket)"udp4"或者"udp6"(UDP v4或者v6)*
事件: 'disconnect'
worker{Worker object}
當(dāng)一個工作進(jìn)程的IPC通道關(guān)閉時會觸發(fā)這個事件。當(dāng)工作進(jìn)程正常退出,被殺死,或者手工關(guān)閉(例如worker.disconnect())時會調(diào)用。
disconnect和exit事件間可能存在延遲。 這些事件可以用來檢測進(jìn)程是否卡在清理過程中,或者存在長連接。
cluster.on('disconnect', function(worker) {
console.log('The worker #' + worker.id + ' has disconnected');
});事件: 'exit'
worker{Worker object}code{Number} 如果正常退出,則為退出代碼.signal{String} 使得進(jìn)程被殺死的信號名 (比如,'SIGHUP')
當(dāng)任意一個工作進(jìn)程終止的時候,集群模塊會觸發(fā)'exit'事件。
可以調(diào)用.fork()重新啟動工作進(jìn)程。
cluster.on('exit', function(worker, code, signal) {
console.log('worker %d died (%s). restarting...',
worker.process.pid, signal || code);
cluster.fork();
});參見child_process event: 'exit'.
事件: 'setup'
settings{Object}
調(diào)用.setupMaster()后會被觸發(fā)。
settings對象就是cluster.settings對象。
詳細(xì)內(nèi)容參見cluster.settings。
cluster.setupMaster([settings])
settings{Object}exec{String} 執(zhí)行文件的路徑。 (默認(rèn)=process.argv[1])args{Array}傳給工作進(jìn)程的參數(shù)列表(默認(rèn)=process.argv.slice(2))silent{Boolean} 是否將輸出發(fā)送給父進(jìn)程的stdio.
setupMaster用來改變默認(rèn)的'fork' 。 一旦調(diào)用,settings值將會出現(xiàn)在cluster.settings里。
你需要注意如下事項:
- 改變?nèi)魏卧O(shè)置,僅會對未來的工作進(jìn)程產(chǎn)生影響,不會影響對目前已經(jīng)運行的進(jìn)程
- 工作進(jìn)程里,僅能改變傳遞給
.fork()的env屬性。 - 以上的默認(rèn)值,僅在第一次調(diào)用的時候有效,之后的默認(rèn)值是調(diào)用
cluster.setupMaster()后的值。
例如:
var cluster = require('cluster');
cluster.setupMaster({
exec: 'worker.js',
args: ['--use', 'https'],
silent: true
});
cluster.fork(); // https worker
cluster.setupMaster({
args: ['--use', 'http']
});
cluster.fork(); // http worker僅能在主進(jìn)程里調(diào)用。
cluster.fork([env])
env{Object} 添加到子進(jìn)程環(huán)境變量中的鍵值。- return {Worker object}
派生一個新的工作進(jìn)程。
僅能在主進(jìn)程里調(diào)用。
cluster.disconnect([callback])
callback{Function} 當(dāng)所有工作進(jìn)程都斷開連接,并且關(guān)閉句柄后被調(diào)用。
cluster.workers里的每個工作進(jìn)程可調(diào)用.disconnect()關(guān)閉。
關(guān)閉所有的內(nèi)部句柄連接,并且沒有任何等待處理的事件時,允許主進(jìn)程優(yōu)雅的退出。
這個方法有一個可選參數(shù),會在完成時被調(diào)用。
僅能在主進(jìn)程里調(diào)用。
cluster.worker
- {Object}
對當(dāng)前工作進(jìn)程對象的引用。主進(jìn)程中不可用。
var cluster = require('cluster');
if (cluster.isMaster) {
console.log('I am master');
cluster.fork();
cluster.fork();
} else if (cluster.isWorker) {
console.log('I am worker #' + cluster.worker.id);
}cluster.workers
- {Object}
存儲活躍工作對象的哈希表,主鍵是id,能方便的遍歷所有工作進(jìn)程,僅在主進(jìn)程可用。
當(dāng)工作進(jìn)程關(guān)閉連接并退出后,將會從cluster.workers里移除。這兩個事件的次序無法確定,僅能保證從cluster.workers移除會發(fā)生在'disconnect'或'exit'之后。
// Go through all workers
function eachWorker(callback) {
for (var id in cluster.workers) {
callback(cluster.workers[id]);
}
}
eachWorker(function(worker) {
worker.send('big announcement to all workers');
});如果希望通過通訊通道引用工作進(jìn)程,那么使用工作進(jìn)程的 id 來查詢最簡單。
socket.on('data', function(id) {
var worker = cluster.workers[id];
});Class: Worker
一個Worker對象包含工作進(jìn)程所有公開的信息和方法。在主進(jìn)程里可用通過cluster.workers來獲取,在工作進(jìn)程里可以通過cluster.worker來獲取。
worker.id
- {String}
每一個新的工作進(jìn)程都有獨立的唯一標(biāo)示,它就是id。
當(dāng)工作進(jìn)程可用時,id就是cluster.workers里的主鍵。
worker.process
- {ChildProcess object}
所有工作進(jìn)程都是通用child_process.fork()創(chuàng)建的,該函數(shù)返回的對象被儲存在process中。
參見: Child Process module
注意:當(dāng)process和.suicide不是true的時候,會觸發(fā)'disconnect'事件,并使得工作進(jìn)程調(diào)用process.exit(0)。它會保護(hù)意外的連接關(guān)閉。
worker.suicide
- {Boolean}
調(diào)用.kill()或.disconnect()后設(shè)置,在這之前是undefined。
worker.suicide能讓你區(qū)分出是自愿的還是意外退出,主進(jìn)程可以根據(jù)這個值,來決定是否是重新派生成工作進(jìn)程。
cluster.on('exit', function(worker, code, signal) {
if (worker.suicide === true) {
console.log('Oh, it was just suicide\' – no need to worry').
}
});
// kill worker
worker.kill();worker.send(message[, sendHandle])
message{Object}sendHandle{Handle object}
這個函數(shù)和child_process.fork()提供的send方法相同。主進(jìn)程里你必須使用這個函數(shù)給指定工作進(jìn)程發(fā)消息。
在工作進(jìn)程里,你也可以用process.send(message)。
這個例子會回應(yīng)所有來自主進(jìn)程的消息:
if (cluster.isMaster) {
var worker = cluster.fork();
worker.send('hi there');
} else if (cluster.isWorker) {
process.on('message', function(msg) {
process.send(msg);
});
}worker.kill([signal='SIGTERM'])
signal{String}發(fā)送給工作進(jìn)程的殺死信號的名稱
這個函數(shù)會殺死工作進(jìn)程。在主進(jìn)程里,它會關(guān)閉worker.process,一旦關(guān)閉會發(fā)送殺死信號。在工作進(jìn)程里,關(guān)閉通道,退出,返回代碼0。
會導(dǎo)致.suicide被設(shè)置。
為了保持兼容性,這個方法的別名是worker.destroy()。
注意,在工作進(jìn)程里有process.kill(),于此不同。
worker.disconnect()
在工作進(jìn)程里,這個函數(shù)會關(guān)閉所有服務(wù)器,等待 'close' 事件,關(guān)閉IPC通道。
在主進(jìn)程里,發(fā)給工作進(jìn)程一個內(nèi)部消息,用來調(diào)用.disconnect()
會導(dǎo)致.suicide被設(shè)置。
注意,服務(wù)器關(guān)閉后,不再接受新的連接,但可以接受新的監(jiān)聽。已經(jīng)存在的連接允許正常退出。當(dāng)連接為空得時候,工作進(jìn)程的IPC通道運行優(yōu)雅的退出。
以上僅能適用于服務(wù)器的連接,客戶端的連接由工作進(jìn)程關(guān)閉。
注意,在工作進(jìn)程里,存在process.disconnect,但并不是這個函數(shù),它是disconnect。
由于長連接可能會阻塞進(jìn)程關(guān)閉連接,有一個較好的辦法是發(fā)消息給應(yīng)用,這樣應(yīng)用會想辦法關(guān)閉它們。超時管理也是不錯,如果超過一定時間后還沒有觸發(fā) disconnect事件,將會殺死進(jìn)程。
if (cluster.isMaster) {
var worker = cluster.fork();
var timeout;
worker.on('listening', function(address) {
worker.send('shutdown');
worker.disconnect();
timeout = setTimeout(function() {
worker.kill();
}, 2000);
});
worker.on('disconnect', function() {
clearTimeout(timeout);
});
} else if (cluster.isWorker) {
var net = require('net');
var server = net.createServer(function(socket) {
// connections never end
});
server.listen(8000);
process.on('message', function(msg) {
if(msg === 'shutdown') {
// initiate graceful close of any connections to server
}
});
}worker.isDead()
工作進(jìn)程結(jié)束,返回true, 否則返回false。
worker.isConnected()
當(dāng)工作進(jìn)程通過IPC通道連接主進(jìn)程時,返回true ,否則false。工作進(jìn)程創(chuàng)建后會連接到主進(jìn)程。當(dāng)disconnect事件觸發(fā)后會關(guān)閉連接。
事件: 'message'
message{Object}
該事件和child_process.fork()所提供的一樣。在主進(jìn)程中您應(yīng)當(dāng)使用該事件,而在工作進(jìn)程中您也可以使用process.on('message')。
例如,有一個集群使用消息系統(tǒng)在主進(jìn)程中統(tǒng)計請求的數(shù)量:
var cluster = require('cluster');
var http = require('http');
if (cluster.isMaster) {
// Keep track of http requests
var numReqs = 0;
setInterval(function() {
console.log("numReqs =", numReqs);
}, 1000);
// Count requestes
function messageHandler(msg) {
if (msg.cmd && msg.cmd == 'notifyRequest') {
numReqs += 1;
}
}
// Start workers and listen for messages containing notifyRequest
var numCPUs = require('os').cpus().length;
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].on('message', messageHandler);
});
} else {
// Worker processes have a http server.
http.Server(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
// notify master about the request
process.send({ cmd: 'notifyRequest' });
}).listen(8000);
}事件: 'online'
和cluster.on('online')事件類似, 僅能在特定工作進(jìn)程里觸發(fā)。
cluster.fork().on('online', function() {
// Worker is online
});不會在工作進(jìn)程里觸發(fā)。
事件: 'listening'
address{Object}
和cluster.on('listening')事件類似, 僅能在特定工作進(jìn)程里觸發(fā)。
cluster.fork().on('listening', function(address) {
// Worker is listening
});不會在工作進(jìn)程里觸發(fā)。
事件: 'disconnect'
和cluster.on('disconnect')事件類似, 僅能在特定工作進(jìn)程里觸發(fā)。
cluster.fork().on('disconnect', function() {
// Worker has disconnected
});事件: 'exit'
code{Number} 正常退出時的退出代碼.signal{String} 使得進(jìn)程被終止的信號的名稱(比如SIGHUP)。
和cluster.on('exit')事件類似, 僅能在特定工作進(jìn)程里觸發(fā)。
var worker = cluster.fork();
worker.on('exit', function(code, signal) {
if( signal ) {
console.log("worker was killed by signal: "+signal);
} else if( code !== 0 ) {
console.log("worker exited with error code: "+code);
} else {
console.log("worker success!");
}
});事件: 'error'
和child_process.fork()事件類似。
工作進(jìn)程里,你也可以用process.on('error')。
網(wǎng)頁名稱:創(chuàng)新互聯(lián)Node.js教程:Node.js 集群
轉(zhuǎn)載來于:http://m.fisionsoft.com.cn/article/cdjoseh.html


咨詢
建站咨詢
