Skip to content

Instantly share code, notes, and snippets.

@yosuke-furukawa
Created September 9, 2012 10:18
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save yosuke-furukawa/3683651 to your computer and use it in GitHub Desktop.
Save yosuke-furukawa/3683651 to your computer and use it in GitHub Desktop.
コードの変更をキャッチして、GracefulなRestartをする方法
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length / 2;
var numWorkers = numCPUs <= 1 ? 2 : numCPUs;
var watch = require('watch');
var domain = require('domain');
var util = require('util');
var forceKilledWorkers = {};
// cluster destroy if timeout.
var destroy = function() {
if (cluster.isWorker) {
var timeout = setTimeout(function() {cluster.worker.destroy();}, 120000);
forceKilledWorkers[cluster.worker.id] = timeout;
}
};
var forkWorkers = function(num) {
var forkDomain = domain.create();
forkDomain.on('error', function(error) {
util.log("error occured when starting new workers.");
util.log(error);
});
forkDomain.run(function(){
for (var i = 0; i < num; i++) {
cluster.fork();
}
});
};
var disconnectCluster = function() {
var disconnectDomain = domain.create();
disconnectDomain.on('error', function(error) {
util.log("error occured when disconnecting new workers.");
util.log(error);
destroy();
});
disconnectDomain.run(function() {
cluster.disconnect();
});
};
var respawnWorkers = function(num) {
disconnectCluster();
forkWorkers(num);
};
watch.createMonitor(__dirname, function (monitor) {
monitor.on("created", function (f, stat) {
respawnWorkers(numWorkers);
});
monitor.on("changed", function (f, curr, prev) {
respawnWorkers(numWorkers);
});
monitor.on("removed", function (f, stat) {
respawnWorkers(numWorkers);
});
});
if (cluster.isMaster) {
forkWorkers(numWorkers);
cluster.on('disconnect', function(worker) {
util.log("worker("+worker.id+").disconnect " + worker.process.pid);
});
cluster.on('exit', function(worker, code, signal) {
if (!worker.suicide) {
cluster.fork();
} else {
util.log("worker("+worker.id+") is suicide.");
}
var timeout = forceKilledWorkers[worker.id];
if (timeout) {
clearTimeout(timeout);
delete forceKilledWorkers[worker.id];
}
util.log("worker("+worker.id+").exit " + worker.process.pid);
});
cluster.on('fork', function(worker) {
util.log("worker("+worker.id+").fork");
});
cluster.on('online', function(worker) {
util.log("worker("+worker.id+").online " + worker.process.pid);
});
cluster.on('listening', function(worker, address) {
util.log("worker("+worker.id+").listening " + address.address + ":" + address.port);
});
} else if (cluster.isWorker) {
var server = http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end("<html><body><h1>Hello World.</h1></body></html>\n");
});
server.listen(3000);
server.on('close', function() { process.exit(); });
} else {
util.log("unsupported cluster.");
}
@yosuke-furukawa
Copy link
Author

Jxckさん、非常に非常に遅くなりましたが、修正してみました。

disconnect してすぐ死ななかったりとかあると、
一時的に worker の数が増えそう。

ベタ書きになっちゃいましたが、2分間、disconnectで失敗した後に強制終了させるようにしてみました。
同様にdomainでforkのエラーも検知するようにしてみました。

また、現在の API では isWorker があるので
else よりそっちを使うほうが、明示してるし個人的には良いかと。
あと、まあほぼ無いと思うけど、将来 master, worker 以外の何かが
出てきた場合云々もあるかって視点でも。

確かに。そうしましょう。でも万が一のworker、master以外の時はどうしようと思って、今は一応
ログに書くだけ、という対応になっています。

あと、今回は割りとふんだんにログを出しているようなので、
suiside あたりのログも出すといいのかも。

suicideもログに出してみました。
あと、suicideじゃないときはなんかの拍子に死んだ時なので、
その時はforkし直すようにしてみました。

Node のマニュアルが元で、こういうサンプルよく見るけど、
本当に全部使っちゃうの?
ってちょっと思いますw

var numCPUs = require('os').cpus().length / 2;
なんかで上のようにしているサンプルを見ました。
これだと半分しか使わない省エネ設定です。

他にも以下の様なのもみました。
var numCPUs = require('os').cpus().length - 2;
これだと必ず2個空けとくパターンですね。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment