dnode是一個node.js的rpc套件,拿來做出一些管理功能還不錯用。
需求
今天在http://www.facebook.com/groups/node.js.tw討論了一下不停止伺服器重新載入修改過模組的可能性,突然想到可以透過cluster與dnode做到。
cluster可以讓伺服器worker跑在子行程,並且在子行程結束時開啟新的子行程。透過這個方式,就可以重新啟動伺服器來載入更動過的程式,但是要遠端來控制還是會有一些困難。這時就要請dnode出馬。
https://github.com/substack/dnode
實作
稍微改過http://nodejs.org/docs/v0.6.0/api/cluster.html提供的官方範例,做出這個簡單的hello world伺服器(dcluster.js):
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
var dnode = require('dnode');
if (cluster.isMaster) {
// Fork workers.
var workers = [],i=0;
for (var i = 0; i < numCPUs; i++) {
workers.push(cluster.fork());
}
cluster.on('death', function(worker) {
console.log('worker ' + worker.pid + ' died.');
var i = 0;
for(;i<workers.length; i++) {
if(worker.pid === workers[i].pid) {
workers.splice(i, 1);
}
}
workers.push(cluster.fork());
});
var manager = dnode({
restart: function(cb) {
var i = 0;
for(; i<workers.length; i++) {
workers[i].send({cmd: 'close'});
console.log('worker ('+workers[i].pid+') going to rebirth.');
}
cb('restart success');
}
});
manager.listen(8000, 'localhost');
} else {
// Worker processes have a http server.
var msg = require('./message');
var server = http.Server(function(req, res) {
res.writeHead(200);
res.end(msg.text);
});
server.listen(8443, 'localhost');
process.on('message', function(msg) {
if(msg && msg.cmd) {
switch(msg.cmd) {
case 'close':
process.nextTick(function(){
server.close();
process.exit();
});
break;
}
}
});
}
這裡利用dnode寫了一個簡單的restart命令,在master收到這個命令後,會透過process.send對子行程送出訊息,格式是{cmd: 'close'}。子行程透過on message事件收到訊息後,就會自殺。接下來在cluster的on death事件中,會重新啟動一個子行程。
送出restart命令的程式非常簡單(dclient.js):
var dnode = require('dnode');
dnode.connect(8000, 'localhost', function(remote) {
remote.restart(function(str) {
console.log(str);
process.exit();
});
});
另外,為了檢測重啟的子行程是否爭的可以載入新的模組,改寫一下res.end()送出的訊息,把它改成由message.js模組中載入:
module.exports = {
text: 'hello world.\n'
};
使用node dcluster.js載入伺服器後,開啟瀏覽器打入網址http://localhost:8443/,就會看到**hello world.**訊息。接下來先修改message.js:
module.exports = {
text: 'hello world too.\n'
};
然後執行node dclient.js命令伺服器重啟,這個時候在瀏覽器重新整理,就會看到訊息變成 hello world too. 。