z-nomp/init.js

259 lines
7.9 KiB
JavaScript

var fs = require('fs');
var os = require('os');
var cluster = require('cluster');
var async = require('async');
var posix = require('posix');
var PoolLogger = require('./libs/logUtil.js');
var BlocknotifyListener = require('./libs/blocknotifyListener.js');
var RedisBlocknotifyListener = require('./libs/redisblocknotifyListener.js');
var WorkerListener = require('./libs/workerListener.js');
var PoolWorker = require('./libs/poolWorker.js');
var PaymentProcessor = require('./libs/paymentProcessor.js');
var Website = require('./libs/website.js');
JSON.minify = JSON.minify || require("node-json-minify");
var portalConfig = JSON.parse(JSON.minify(fs.readFileSync("config.json", {encoding: 'utf8'})));
var logger = new PoolLogger({
logLevel: portalConfig.logLevel
});
try {
require('newrelic');
if (cluster.isMaster)
logger.debug('NewRelic', 'Monitor', 'New Relic initiated');
} catch(e) {}
//Try to give process ability to handle 100k concurrent connections
try{
posix.setrlimit('nofile', { soft: 100000, hard: 100000 });
}
catch(e){
logger.warning('POSIX', 'Connection Limit', '(Safe to ignore) Must be ran as root to increase resource limits');
}
if (cluster.isWorker){
switch(process.env.workerType){
case 'pool':
new PoolWorker(logger);
break;
case 'paymentProcessor':
new PaymentProcessor(logger);
break;
case 'website':
new Website(logger);
break;
}
return;
} /* else {
var coinNames = ['alphacoin','frankocoin','emerald','kittehcoin'];
var curIndex = 0;
setInterval(function () {
var newCoinName = coinNames[++curIndex % coinNames.length];
console.log("SWITCHING to "+newCoinName);
var ipcMessage = {type:'switch', coin: newCoinName};
Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].send(ipcMessage);
});
}, 20000);
} */
//Read all pool configs from pool_configs and join them with their coin profile
var buildPoolConfigs = function(){
var configs = {};
fs.readdirSync('pool_configs').forEach(function(file){
var poolOptions = JSON.parse(JSON.minify(fs.readFileSync('pool_configs/' + file, {encoding: 'utf8'})));
if (!poolOptions.enabled) return;
var coinFilePath = 'coins/' + poolOptions.coin;
if (!fs.existsSync(coinFilePath)){
logger.error('Master', poolOptions.coin, 'could not find file: ' + coinFilePath);
return;
}
var coinProfile = JSON.parse(JSON.minify(fs.readFileSync(coinFilePath, {encoding: 'utf8'})));
poolOptions.coin = coinProfile;
configs[poolOptions.coin.name] = poolOptions;
});
return configs;
};
var spawnPoolWorkers = function(portalConfig, poolConfigs){
Object.keys(poolConfigs).forEach(function(coin){
var p = poolConfigs[coin];
var internalEnabled = p.shareProcessing && p.shareProcessing.internal && p.shareProcessing.internal.enabled;
var mposEnabled = p.shareProcessing && p.shareProcessing.mpos && p.shareProcessing.mpos.enabled;
if (!internalEnabled && !mposEnabled){
logger.error('Master', coin, 'Share processing is not configured so a pool cannot be started for this coin.');
delete poolConfigs[coin];
}
});
if (Object.keys(poolConfigs).length === 0){
logger.warning('Master', 'PoolSpawner', 'No pool configs exists or are enabled in pool_configs folder. No pools spawned.');
return;
}
var serializedConfigs = JSON.stringify(poolConfigs);
var numForks = (function(){
if (!portalConfig.clustering || !portalConfig.clustering.enabled)
return 1;
if (portalConfig.clustering.forks === 'auto')
return os.cpus().length;
if (!portalConfig.clustering.forks || isNaN(portalConfig.clustering.forks))
return 1;
return portalConfig.clustering.forks;
})();
var createPoolWorker = function(forkId){
var worker = cluster.fork({
workerType: 'pool',
forkId: forkId,
pools: serializedConfigs,
portalConfig: JSON.stringify(portalConfig)
});
worker.on('exit', function(code, signal){
logger.error('Master', 'PoolSpanwer', 'Fork ' + forkId + ' died, spawning replacement worker...');
setTimeout(function(){
createPoolWorker(forkId);
}, 2000);
});
};
var i = 0;
var spawnInterval = setInterval(function(){
createPoolWorker(i);
i++;
if (i === numForks){
clearInterval(spawnInterval);
logger.debug('Master', 'PoolSpawner', 'Spawned ' + Object.keys(poolConfigs).length + ' pool(s) on ' + numForks + ' thread(s)');
}
}, 250);
};
var startWorkerListener = function(poolConfigs){
var workerListener = new WorkerListener(logger, poolConfigs);
workerListener.init();
};
var startBlockListener = function(portalConfig){
//block notify options
//setup block notify here and use IPC to tell appropriate pools
var listener = new BlocknotifyListener(portalConfig.blockNotifyListener);
listener.on('log', function(text){
logger.debug('Master', 'Blocknotify', text);
});
listener.on('hash', function(message){
var ipcMessage = {type:'blocknotify', coin: message.coin, hash: message.hash};
Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].send(ipcMessage);
});
});
listener.start();
};
var startRedisBlockListener = function(portalConfig){
//block notify options
//setup block notify here and use IPC to tell appropriate pools
if (!portalConfig.redisBlockNotifyListener.enabled) return;
var listener = new RedisBlocknotifyListener(portalConfig.redisBlockNotifyListener);
listener.on('log', function(text){
logger.debug('Master', 'blocknotify', text);
}).on('hash', function (message) {
var ipcMessage = {type:'blocknotify', coin: message.coin, hash: message.hash};
Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].send(ipcMessage);
});
});
listener.start();
};
var startPaymentProcessor = function(poolConfigs){
var enabledForAny = false;
for (var pool in poolConfigs){
var p = poolConfigs[pool];
var enabled = p.enabled && p.shareProcessing && p.shareProcessing.internal && p.shareProcessing.internal.enabled;
if (enabled){
enabledForAny = true;
break;
}
}
if (!enabledForAny)
return;
var worker = cluster.fork({
workerType: 'paymentProcessor',
pools: JSON.stringify(poolConfigs)
});
worker.on('exit', function(code, signal){
logger.error('Master', 'Payment Processor', 'Payment processor died, spawning replacement...');
setTimeout(function(){
startPaymentProcessor(poolConfigs);
}, 2000);
});
};
var startWebsite = function(portalConfig, poolConfigs){
if (!portalConfig.website.enabled) return;
var worker = cluster.fork({
workerType: 'website',
pools: JSON.stringify(poolConfigs),
portalConfig: JSON.stringify(portalConfig)
});
worker.on('exit', function(code, signal){
logger.error('Master', 'Website', 'Website process died, spawning replacement...');
setTimeout(function(){
startWebsite(portalConfig, poolConfigs);
}, 2000);
});
};
(function init(){
var poolConfigs = buildPoolConfigs();
spawnPoolWorkers(portalConfig, poolConfigs);
startPaymentProcessor(poolConfigs);
startBlockListener(portalConfig);
startRedisBlockListener(portalConfig);
startWorkerListener(poolConfigs);
startWebsite(portalConfig, poolConfigs);
})();