Showing posts with label cluster. Show all posts
Showing posts with label cluster. Show all posts

Sunday, March 24, 2019

nodejs require('net') cluster server emit connection to express app with http, https modules website chat bot

app.js



var cluster = require('cluster'), net = require('net');
var port = process.env.PORT || 8080;
var num_processes = process.env.WORK || require('os').cpus().length;
var fs = require('fs');
var options = {
pauseOnConnect: true,
key: fs.readFileSync(__dirname + '/private/server.key', 'utf8'),
cert: fs.readFileSync(__dirname + '/private/server.crt', 'utf8'),
requestCert: true,
rejectUnauthorized: false
}
if (cluster.isMaster) {
// This stores our workers. We need to keep them to be able to reference
// them based on source IP address. It's also useful for auto-restart, for example.
var workers = [];

// Helper function for spawning worker at index 'i'.
var spawn = function (i) {
workers[i] = cluster.fork();

// Optional: Restart worker on exit
workers[i].on('exit', function (code, signal) {
console.log('respawning worker', i);
spawn(i);
});
};

// Spawn workers.
for (var i = 0; i < num_processes; i++) {
spawn(i);
}

// Helper function for getting a worker index based on IP address.
// This is a hot path so it should be really fast. The way it works
// is by converting the IP address to a number by removing non numeric
// characters, then compressing it to the number of slots we have.
//
// Compared against "real" hashing (from the sticky-session code) and
// "real" IP number conversion, this function is on par in terms of
// worker index distribution only much faster.
var worker_index = function (ip, len) {
var s = '';
for (var i = 0, _len = ip.length; i < _len; i++) {
if (!isNaN(ip[i])) {
s += ip[i];
}
}
return Number(s) % len;
};

// Create the outside facing server listening on our port.
var server = net.createServer({ pauseOnConnect: true }, function (connection) {
// We received a connection and need to pass it to the appropriate
// worker. Get the worker for this connection's source IP and pass
// it the connection.
var worker = workers[worker_index(connection.remoteAddress, num_processes)];
worker.send('sticky-session:connection', connection);
}).listen(port);

console.log('Master server running on port: ' + port);
} else {
/*nodepro process running...*/
var core = require('./nodepro/core/start');
//if not use options with ssl then ERR_SSL_PROTOCOL_ERROR
var server = core.run(options);

// Listen to messages sent from the master. Ignore everything else.
process.on('message', function (message, connection) {
if (message !== 'sticky-session:connection') {
return;
}
//console.log(message, connection.remoteAddress);

// Emulate a connection event on the server by emitting the
// event with the connection the master sent us.
server.emit('connection', connection);

connection.resume();
});

console.log('ProcId : ' + process.pid + ' is listening to all incoming requests...');
}



nodepro/core/start.js



/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/


exports.run = function (credentials) {
// don't use yet in BOT
//var loader = require('./loader').instance();
// don't use yet
/*
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
var http = app.listen(80, function () {
console.log('ProcId : ' + process.pid + ' is listening to all incoming requests...');
});
*/

// Note we don't use a port here because the master listens on it for us.
var express = require('express'), path = require('path'),
http = require('http'), https = require('https');
//var app = express.createServer(credentials);
var app = new express();
// Don't expose our internal server to the outside.
var server = null;
if (credentials == undefined)
server = http.createServer(app);
else
server = https.createServer(credentials, app);
//var server = app.listen();

/*app use modules[web]*/
//var web_core = loader.module('web');
//web_core.init(express, app);
//or
app.use(express.static('public'));

/*server use modules[chat]*/
/*
if (loader.options.modules.chat.enable) {
var chat = loader.module('chat');
chat.init(server, app);
}
*/
/*server use modules[gamebot]*/
//var bot = loader.module('gamebot');
var bot = require('../modules/gamebot');
bot.init(server, app);

return server;
};



nodepro/modules/web.js


exports.init = function (express, app) {
var loader_package = require('../core/loader');
var loader = loader_package.instance();
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var sys_config = loader.config('sys.config');
var _opt = loader.options;

/*app.configuring*/
//set NODE_ENV=production&&node app.js
function debuggerModule(req, res, next) {
return next();
}
// debugging middleware in development only
if ('development' === app.get('env')) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
app.use(debuggerModule);
}
// process.env['CONSUMER_KEY'] = ""
// process.env['CONSUMER_SECRET'] = ""
// process.env['ACCESS_TOKEN_KEY'] = ""
// process.env['ACCESS_TOKEN_SECRET'] = ""

app.use(bodyParser.urlencoded({ extended: _opt.fw.urlencoded_extended }));// parse application/x-www-form-urlencoded
app.use(bodyParser.json());// parse application/json
app.set('json spaces', 1);
app.use(cookieParser());
app.use(_opt.res.static_path, express.static(loader.static_dir()));

if (_opt.fw.jade_engine) {
app.engine('jade', require('jade').__express);
app.set('view engine', 'jade');
}
if (_opt.fw.disable_view_cache) {
app.disable('view cache');
} else {
app.set('view cache', true);
}
if (_opt.fw.disable_etag) {
app.disable('etag');
}
if (_opt.fw.use_compression) {
var compression = require('compression');
app.use(compression());
}
if (_opt.fw.use_helmet) {
var helmet = require('helmet');
app.use(helmet());
app.use(helmet.noCache());
app.use(helmet.frameguard());
} else {
app.disable('x-powered-by');
}
if (_opt.fw.use_session) {
var sessionLib = loader.lib('session/' + _opt.fw.session_lib);
sessionLib.configure(express, app, loader);
}

/*app.routing*/
function init_loader_base(area_item, app_area) {
var new_loader = loader_package.instance();
var area_name = new_loader._comm.trim(app_area.mountpath, '/');
var base = {};
base.bag = {};
base.loader = new_loader;
base.area = area_name;
base.area_object = area_item;
base.app = app_area;
new_loader._base = base;
return new_loader._base;
}
function parallel(middlewares) {
var async = require("async");
return function (req, res, next) {
async.each(middlewares, function (mw, cb) {
mw(req, res, cb);
}, next);
};
}
function headers(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
}
function routing(req, res, next) {
console.log('routing..................');
headers(req, res, next);
var base = init_loader_base(undefined, app);
var router_package = base.loader.core('router');
var instance = router_package();
instance.route(base, req, res, next);
}
// Router middleware, mentioned it before defining routes.
/*areas*/
sys_config.areas.forEach(function (area_item) {
var app_area = new express();
app.use(area_item.uri, app_area);
app_area.use(function (req, res, next) {
headers(req, res, next);
var base = init_loader_base(area_item, app_area);
var router_package = base.loader.core('router');
var instance = router_package();
instance.route(base, req, res, next);
});
});
/*main*/
if (_opt.fw.use_parallel) {
app.use(parallel([routing]));
} else {
app.use(routing);
}
};



nodepro/modules/gamebot.js


exports.init = function (server, app) {
//var loader_package = require('../core/loader');
//var loader = loader_package.instance();
var socketio = require('socket.io');
//var socket_redis = require('socket.io-redis');
var io = socketio(server);
//var _opt = loader.options;
// Tell Socket.IO to use the redis adapter. By default, the redis
// server is assumed to be on localhost:6379. You don't have to
// specify them explicitly unless you want to change them.
//io.adapter(socket_redis({ host: 'localhost', port: 6379 }));
var botio = io.of('/bot'/*_opt.modules.gamebot.uri*/);

var uuidv4 = require('uuid/v4');
var rooms = {};
var sockets = {};
botio.on('connection', function (socket) {
//console.log('- io.sockets.server.engine.clientsCount = ', io.sockets.server.engine.clientsCount, ' -> ', sockets, ';');

socket.emit('detect', /* send to client */
{ uuid: uuidv4() }
); // emit an event to the socket
socket.on('context', function (data) { /* */
if (rooms[data.context] == undefined) {
rooms[data.context] = {};
}
rooms[data.context][data.player_id] = socket;
sockets[socket.id] = [data.context, data.player_id];
console.log('Connected:: ', data.context, ', ', data.player_id);
});
socket.on('message', function (data) { /* */
var room = rooms[data.context];
var players = Object.keys(room);
players.forEach(function (player_id) {
var friend_socket = room[player_id];
friend_socket.emit('message', data);
});
//var sockets_keys = Object.keys(sockets);
//console.log('sockets.length is ', sockets_keys.length);
//botio.emit('message', data);
// emit an event to all connected sockets
}); // listen to the event

socket.on('disconnect', function () {
var meta = sockets[socket.id];
console.log('(meta != undefined) is ', (meta != undefined));
if (meta != undefined) {
var context = meta[0];
var player_id = meta[1];
console.log('context.length is ', context, ', ', Object.keys(rooms[context]).length);
delete sockets[socket.id];
delete rooms[context][player_id];
console.log('context.length is ', context, ', ', Object.keys(rooms[context]).length);
}
var sockets_keys = Object.keys(sockets);
console.log('sockets.length is ', sockets_keys.length);
console.log('Disconnected:: ', socket.id);
});
});
};



public/index.html
.

<!DOCTYPE html>
<html>

<head>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
</head>

<body>
<script>
$(function () {
function getOrContextId() {
return 'room1';
}
$('#txt').val(window.location);
var my_id = undefined;
var socket = io(window.location + 'bot');
socket.on('detect', function (data) {
my_id = "ID:" + data.uuid;
socket.emit('context', { context: getOrContextId(), player_id: my_id });
$('ul').append($('<li>').html('Me-' + my_id + ': i connected'));
});
socket.on('message', function (data) {
if (data.text !== undefined) {
if (my_id !== data.player_id)
$('ul').append($('<li>').html('Friend-' + data.player_id + ': ' + data.text));
else
$('ul').append($('<li>').html('Me-' + my_id + ': ' + data.text));
}
});
$('#btn').click(function () {
socket.emit('message', {
context: getOrContextId(),
player_id: my_id,
text: $('#txt').val()
});
});
});
</script>
<input type="text" id="txt">
<a id="btn" href="javascript:void(0);">Send</a>
<ul>
<li></li>
</ul>
</body>

</html>