cluster-service
Version:
Turns your single process code into a fault-resilient multi-process service with built-in REST & CLI support
141 lines (117 loc) • 4.07 kB
JavaScript
var cservice = require("../cluster-service");
var cluster = require("cluster");
var fs = require("fs");
var path = require("path");
var httpProxy = require('http-proxy');
var msgBus = require('./message-bus');
cservice.workerReady(false);
var proxy = httpProxy.createProxyServer({});
var versionHeader = cluster.worker.env.versionHeader;
var versionPath = cluster.worker.env.versionPath;
var bindingInfo = JSON.parse(cluster.worker.env.bindingInfo);
var workerFilename = cluster.worker.env.workerFilename;
var waiters = {};
cservice.locals.proxy.defaultVersion = null; // default not set til online
// set initial versions based on state provided by master
cservice.locals.proxy.versions = JSON.parse(cluster.worker.env.versions);
var proxyServer;
if (bindingInfo.tlsOptions) {
// https
if (typeof bindingInfo.tlsOptions.key === "string") {
bindingInfo.tlsOptions.key = fs.readFileSync(bindingInfo.tlsOptions.key);
}
if (typeof bindingInfo.tlsOptions.cert === "string") {
bindingInfo.tlsOptions.cert = fs.readFileSync(bindingInfo.tlsOptions.cert);
}
if (typeof bindingInfo.tlsOptions.pem === "string") {
bindingInfo.tlsOptions.pem = fs.readFileSync(bindingInfo.tlsOptions.pem);
}
proxyServer = require("https").createServer(
bindingInfo.tlsOptions, proxyServerRequest
);
} else {
// http
proxyServer = require("http").createServer(proxyServerRequest);
}
process.on("message", onMessageFromMaster);
function proxyServerRequest(req, res) {
var versionStr = req.headers[versionHeader] ||
cservice.locals.proxy.options.defaultVersion
;
getProxyVersion(versionStr, function (err, version) {
if (err) {
cservice.log("Failed to load proxy version " + versionStr, err);
// todo: add option to set 404 content
// via options.customResponses[404] file
res.writeHead(404);
return void res.end("Not found");
}
proxy.web(req, res, { target: "http://127.0.0.1:" + version.port });
});
}
proxyServer.listen(bindingInfo.port, cservice.workerReady);
function getProxyVersion(versionStr, cb) {
var version = cservice.locals.proxy.versions[versionStr];
if (version) {
updateVersionLastAccess(version);
return waitForVersionToComeOnline(versionStr, cb);
}
// version not found, lets start it
cservice.trigger("proxy", function(err, result) {
if (err) {
return cb(err);
}
// return newly started version
waitForVersionToComeOnline(versionStr, cb);
}, "version", versionStr);
}
function waitForVersionToComeOnline(versionStr, cb) {
var version = cservice.locals.proxy.versions[versionStr];
if (version && version.online === true) {
return cb(null, version); // ready!
}
// try waiting
var timer, attempts = 0;
timer = setInterval(function() {
version = cservice.locals.proxy.versions[versionStr];
if (version && version.online === true) {
clearInterval(timer);
return cb(null, version);
}
attempts++;
if (attempts > 240) {
clearInterval(timer);
return cb("Timed out waiting for version to come online!");
}
}, 250);
timer.unref();
}
function onMessageFromMaster(msg) {
if (!msgBus.isValidMessage(msg)) return;
switch (msg.cservice.cmd) {
case "proxyVersions":
// update versions state
cservice.locals.proxy.versions = msg.cservice.versions;
cservice.locals.proxy.options.defaultVersion =
msg.cservice.defaultVersion
;
// update version for default port mapping
cservice.locals.proxy.defaultVersion =
cservice.locals.proxy.versions[msg.cservice.defaultVersion]
;
break;
}
}
function updateVersionLastAccess(version) {
var now = Date.now();
var diff = now - version.lastAccess;
if (diff >= 0 && diff < 5000) {
return; // if less than 5 seconds since last access,
// don't bother updating master
}
version.lastAccess = now;
var msg = cservice.msgBus.createMessage("versionUpdateLastAccess", {
version: version.name
});
cservice.processSafeSend(process, msg);
}