greenlock-express.js/master.js

161 lines
3.0 KiB
JavaScript
Raw Normal View History

2019-10-27 05:52:19 +00:00
"use strict";
require("./main.js");
var Master = module.exports;
var cluster = require("cluster");
var os = require("os");
2019-10-28 07:06:43 +00:00
var msgPrefix = "greenlock:";
2019-10-27 05:52:19 +00:00
Master.create = function(opts) {
var resolveCb;
2019-10-28 09:43:42 +00:00
var _readyCb;
2019-10-27 05:52:19 +00:00
var _kicked = false;
2019-10-27 09:59:49 +00:00
var greenlock = require("./greenlock.js").create(opts);
2019-10-27 05:52:19 +00:00
var ready = new Promise(function(resolve) {
resolveCb = resolve;
}).then(function(fn) {
2019-10-28 09:43:42 +00:00
_readyCb = fn;
return fn;
2019-10-27 05:52:19 +00:00
});
function kickoff() {
if (_kicked) {
return;
}
_kicked = true;
2019-10-28 07:06:43 +00:00
Master._spawnWorkers(opts, greenlock);
2019-10-27 05:52:19 +00:00
ready.then(function(fn) {
// not sure what this API should be yet
2019-10-28 07:06:43 +00:00
fn();
2019-10-27 05:52:19 +00:00
});
}
var master = {
2019-10-28 07:06:43 +00:00
serve: function() {
2019-10-27 05:52:19 +00:00
kickoff();
return master;
},
master: function(fn) {
2019-10-28 09:43:42 +00:00
if (_readyCb) {
2019-10-27 05:52:19 +00:00
throw new Error("can't call master twice");
}
kickoff();
resolveCb(fn);
return master;
}
};
2019-10-28 09:43:42 +00:00
return master;
2019-10-27 05:52:19 +00:00
};
function range(n) {
2019-10-28 07:06:43 +00:00
n = parseInt(n, 10);
if (!n) {
return [];
}
2019-10-27 05:52:19 +00:00
return new Array(n).join(",").split(",");
}
2019-10-28 07:06:43 +00:00
Master._spawnWorkers = function(opts, greenlock) {
2019-10-27 05:52:19 +00:00
var numCpus = parseInt(process.env.NUMBER_OF_PROCESSORS, 10) || os.cpus().length;
2019-10-28 07:06:43 +00:00
// process rpc messages
// start when dead
2019-10-27 05:52:19 +00:00
var numWorkers = parseInt(opts.numWorkers, 10);
if (!numWorkers) {
if (numCpus <= 2) {
2019-10-28 09:43:42 +00:00
numWorkers = 2;
2019-10-27 05:52:19 +00:00
} else {
numWorkers = numCpus - 1;
}
}
2019-10-28 09:52:38 +00:00
cluster.on("exit", function() {
setTimeout(function() {
process.exit(3);
}, 100);
});
2019-10-28 09:43:42 +00:00
var workers = range(numWorkers);
function next() {
if (!workers.length) {
return;
}
workers.pop();
// for a nice aesthetic
setTimeout(function() {
Master._spawnWorker(opts, greenlock);
next();
}, 250);
}
next();
2019-10-28 07:06:43 +00:00
};
Master._spawnWorker = function(opts, greenlock) {
var w = cluster.fork();
// automatically added to master's `cluster.workers`
w.on("exit", function(code, signal) {
// TODO handle failures
// Should test if the first starts successfully
// Should exit if failures happen too quickly
// For now just kill all when any die
if (signal) {
console.error("worker was killed by signal:", signal);
} else if (code !== 0) {
console.error("worker exited with error code:", code);
} else {
console.error("worker unexpectedly quit without exit code or signal");
}
process.exit(2);
//addWorker();
2019-10-27 05:52:19 +00:00
});
2019-10-28 07:06:43 +00:00
function handleMessage(msg) {
if (0 !== (msg._id || "").indexOf(msgPrefix)) {
return;
}
if ("string" !== typeof msg._funcname) {
// TODO developer error
return;
}
function rpc() {
return greenlock[msg._funcname](msg._input)
.then(function(result) {
w.send({
_id: msg._id,
_result: result
});
})
.catch(function(e) {
var error = new Error(e.message);
Object.getOwnPropertyNames(e).forEach(function(k) {
error[k] = e[k];
});
w.send({
_id: msg._id,
_error: error
});
});
}
try {
rpc();
} catch (e) {
console.error("Unexpected and uncaught greenlock." + msg._funcname + " error:");
console.error(e);
}
}
w.on("message", handleMessage);
2019-10-27 05:52:19 +00:00
};