Same data object for all child processes with vanilla nodejs - javascript

I'm trying to create a server that receives the request and sends it to the servers running on different ports. All child processes servers are the same, they just have different ports.
For example, port 4000 listens for a request, and sends it to the 4001 port which prepares a response and sends the response back to the 4000 port and from the 4000 port to the client and next requests 4000 sends to 4002 and so on.
But I want all processes to work with the same simple data object (without installing any packages) which is stored in a separate file that means if the 4001 port changes this object, the 4002 port should work with this changed object without creating a new one in memory.
Here is the sample code-
const cluster = require("cluster");
const http = require("http");
const os = require("os");
let port = 4000;
let current = 1;
if (cluster.isPrimary) {
const redirectHandler = (req, response) => {
const request = http.request(
{ ...req, path: req.url, port: 4000 + current },
(res) => {
res.pipe(response);
}
);
request.end();
if (current === os.cpus().length - 1) {
current = 0;
} else {
current++;
}
};
const server = http.createServer(redirectHandler);
server.listen(port, () => port++);
for (let i = 0; i < os.cpus().length - 1; i++) {
const worker = cluster.fork({ TASK_PORT: ++port });
}
}
if (cluster.isWorker) {
const workerPort = process.env["TASK_PORT"];
const workerServer = http.createServer((req, res) => {
/* change JS object which is located in separate file */
res.end("Text" + workerPort);
});
workerServer.listen(+workerPort);
}

Related

How to stop node.js functions/loop via request

I'm running on a fastify server and when I send a request, I get a ton of data from mongodb and start a for loop to process each item. This can take ~ 30 minutes. Each item is "processed" by sending to ffmpeg, redis pub->sub, and then a socket to the client.
// Streams controller
exports.renderStreams = async function (req, reply) {
const streams = await Stream.find({}).sort({ createdAt: -1 })//.limit(5)
const renderedStreams = renderStreams(this, streams);
return { success: true, streams: streams.length };
}
// renderStreams
const renderStreams = (fastify, streams = []) => {
const { redis } = fastify;
const channel = "streams";
for (let i = 0; i < streams.length; i++) {
setTimeout(async () => {
const stream = streams[i];
await renderStream(redis, channel, stream);
}, i * 200)
}
}
I am wondering in this for loop, how can I either "pause" it or stop it completely (or both?) via another request, maybe when I call /api/streams/stop.
How would this be possible?
You can use socket.io to do communications with your script while it is still running, so you would create a function to stop the loop and when you get the notification from socket.io you would run it. Documentation link: https://socket.io/docs/v4/

Room not found/ accessible in io object

Currently trying to learn how socket.io works and to create a room based game, but having trouble to get clients to the same room after trying to move my code in a seperated file.
If I use the same code from game.js in my app.js file within io.on("connection")... i´m able to access the room and put players in the same room.
app.js
const express = require("express");
const cors = require("cors");
var http = require("http");
const game = require("./core/game/game");
const app = express();
const port = process.env.PORT || 4001;
const index = require("./routes/index");
const server = http.createServer(app);
const io = require("socket.io")(server, {
cors: {
origin: "*",
},
});
/
app.use(cors({ origin: "*" }));
app.use("/", index);
// Reduce the logging output of Socket.IO
/* io.set('log level',1); */
io.on("connection", (socket) => {
console.log("New client connected");
game.initGame(io, socket);
});
server.listen(port, () => {
console.log(`listening on *:${port}`);
});
game.js
var io;
var gameSocket;
/**
* This function is called by index.js to initialize a new game instance.
*
* #param sio The Socket.IO library
* #param socket The socket object for the connected client.
*/
exports.initGame = function (sio, socket) {
io = sio;
gameSocket = socket;
gameSocket.emit("connected", { message: "You are connected!" });
// Host Events
gameSocket.on("hostCreateNewGame", createRoom);
/* gameSocket.on("hostRoomFull", hostPrepareGame);
gameSocket.on("hostCountdownFinished", hostStartGame);
gameSocket.on("hostNextRound", hostNextRound); */
// Player Events
gameSocket.on("playerJoinGame", addPlayer);
/* gameSocket.on("playerAnswer", playerAnswer);
gameSocket.on("playerRestart", playerRestart); */
};
async function createRoom(data) {
console.log("Create Session");
console.log(data);
console.log(data.username);
var gameId = (Math.random() * 100000) | 0;
console.log(data.username);
console.log(gameId);
//gameSocket.username = username;
gameSocket.join(gameId);
}
async function addPlayer(data) {
console.log("JOIN Session");
console.log(data.gameId);
//console.log(socket.username);
const clients = await io.in(data.gameId).allSockets();
console.log(clients);
if (!clients) {
console.error("[INTERNAL ERROR] Room creation failed!");
}
if (clients.size === 0) {
console.log("room does not exist");
return;
}
console.log(await io.in(data.gameId).allSockets());
gameSocket.username = data.username;
gameSocket.join(gameId);
console.log(await io.in(data.gameId).allSockets());
io.to(data.gameId).emit("joinSuccess", { message: "JUHU" });
If I try to use this code, the clients are always undefined which means I cannot the room from my current io object
const clients = await gameSocket.in(data.gameId).allSockets(); //undefined
Can someone show me what I would need to change in order to access the right io object and find the rooms. Maybe I´m trying to follow a bad approach here when trying to seperate the code from my app.js file.
Any help would be great.
Got this working finally.
The issue was that this returned a real number
var gameId = (Math.random() * 100000) | 0;
Whereas the value send to addPlayers function was a String...

Manage multiple Websocket client connection with node.js

I'm implementing a websocket client in node and my webhook's trying to handle multiple connections from a chatbot service. For example: a new user income, a websocket connection is established on an external chatbot service. The websocket URL is obtained through XMLHttpRequest in my code. And then I use this url to connect to the chatbot service using the ws object (new WebSocket('wssUrlObtainedThroughAjaxRequest','default-protocol')). So each user have a WebSocket. The question is that my code runs sequentially. So if two people sends message to my webhook node, things don't works properly because parallelism. Well, I'll post example code here to make it better to understand.
const express = require('express');
const PORT = process.env.PORT || 8002;
let WebSocket = require('ws');
let CONNECTIONS = new Map();
...
...
...
const app = express()
.use(bodyparser.urlencoded({extended: false}))
.use(bodyparser.json());
app.post('/', (req, res) => {
...
...
...
} else if (req.body.type === 'MESSAGE') {
let DM = req.body.space.name;
let msg = req.body.message.text;
ws = (CONNECTIONS.get(DM)!=null) ? CONNECTIONS.get(DM) : null;
if(ws==null || ws.readyState==3){
controlws.gerarURLWS();
ws = new WebSocket(controlws.urlws, 'talk-protocol');
CONNECTIONS.set(DM,ws);
}
// Executes on websocket openning
ws.onopen = function (event) {
console.log('Canal aberto;');
keepAliveWS();
ws.send(JSON.stringify(msgKoreAi(msg)));
}
if(ws.readyState==1)
ws.send(JSON.stringify(msgKoreAi(msg)));
ws.onmessage = async function (event) {
let resp = JSON.parse(event.data);
if (resp.type == "bot_response") {
text = resp.message[0].component.payload.text;
if(text==null){ // tem quick reply
//mount card hangouts response
let qreplies = resp.message[0].component.payload.payload.quick_replies;
card = '{"sections": [{"widgets": [{"buttons": [';
for(let i=0; i<qreplies.length; i++){
if(i!=qreplies.length-1)
card+='{"textButton": {"text": "'+qreplies[i].payload+'","onClick": {"action": {"actionMethodName": "'+qreplies[i].payload+'"}}}},';
else
card+='{"textButton": {"text": "'+qreplies[i].payload+'","onClick": {"action": {"actionMethodName": "'+qreplies[i].payload+'"}}}}';
}
card+=']}]}],"name": "respostas"}';
card = JSON.parse(card);
text = resp.message[0].component.payload.payload.text;
{
await assyncMessage(DM, text);
await assyncMessage(DM, card);
}
return;
}
//Send assync messages if synchronous was already sent
if(res.headersSent){
{
return await assyncMessage(DM, text);
}
}
else
return res.json({text});
}
}
return;
}
...
...
...
app.listen(PORT, () => {
console.log(`Server is running in port - ${PORT}`);
});

Why does my websocket close after a few minutes?

I'm using ws with node.js on the server side and the regular WebSocket API on the client side. Opening the connection and messaging a few times back and forth works fine. But the socket always closes after a minute or two. Aren't they supposed to persist? Am I doing something wrong?
My server is node.js hosted on heroku. I just tested locally again using foreman start (the heroku tool to run the server locally) and the socket doesn't close unexpectedly at all, so perhaps it's a misconfiguration on heroku. Anyway, here's a relevant code sample with a few functions omitted for brevity.
I'm testing the application in Chrome on OSX Yosemite but have seen the same behavior in Chrome on Windows 7 when running against production environment.
server:
// Client <-> Host Protocol functions. Move to a different file so that they can be shared.
var C2H_SIGNAL_TYPE_REGISTER = "register";
var H2C_SIGNAL_WELCOME = "welcome";
var H2C_SIGNAL_TYPE_ERROR = "error";
var H2C_SIGNAL_TYPE_PEER_ADDED = "peer_joined";
var H2C_SIGNAL_TYPE_PEER_LEFT = "peer_left";
// Update channel endpoint names.
var UPDATE_ENDPOINT_PEERS = "/peers";
// Create a signal message with all asociated default properties.
// Signal senders should create this object and update it accordingly when
// building a signal message to send to a peer.
function createHostMsg(type)
{
var msg = { signalType: type };
if ( type == H2C_SIGNAL_WELCOME ) {
// Since we're sending a welcome message, we need to provide a list
// of currently connected clients.
msg.peers = {};
for ( var addr in clients ) {
console.log("addr " + addr);
var c = clients[addr].description;
if ( c && c.id ) {
msg.peers[c.id] = c;
}
}
}
return msg;
}
// require modules.
var express = require('express');
var http = require('http');
var bodyParser = require('body-parser');
var multer = require('multer');
// Tracks connected peers.
var clients = { };
// 1. Configure the application context settings.
var app = express();
app.enable('trust proxy');
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.json()); // parse json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.use(multer()); // for parsing multipart/form-data
// a. configure http routers. these will handle requests coming from app.
app.set('port', (process.env.PORT || 5000));
app.get('/app', peerApp);
app.get('/script/:name', publicScriptRouter);
// 2. Create the http server itself, passing app to be the request handler.
// app will handle routing and multiplexing of incoming requests to different
// route middleware handlers.
var http = require('http');
var WebSocketServer = require("ws").Server
var httpServer = http.createServer(app);
httpServer.listen( app.get('port') );
// 3. Create one of these for all socket endpoints.
var wss = new WebSocketServer( { server: httpServer, path: UPDATE_ENDPOINT_PEERS } );
wss.on("connection", function(webSocket) {
// 1. Associate the socket with the remote address it came from.
var remoteAddress = webSocket._socket.remoteAddress;
var remotePort = webSocket._socket.remotePort;
var clientConnID = remoteAddress + ":" + remotePort;
var exists = clients[clientConnID] != null;
if ( exists ) {
console.log("socket server connection: associating new connection from %s with registered peer.", clientConnID);
clients[clientConnID].socket = webSocket;
} else {
console.log("socket server connection: associating new connection from %s with unregistered peer.", clientConnID);
clients[clientConnID] = { description: null, socket: webSocket };
}
// 2. Hook up handlers for communication over this particular socket.
webSocket.on("message", function(data, flags) {
processMessage(webSocket, data, flags);
});
webSocket.on("close", function() {
// Praise satin for closures!!
removePeer(clientConnID);
});
});
// Transduce the message and handle it accordingly.
function processMessage(socket, data, flags)
{
var msg = JSON.parse(data);
if ( !msg.signalType ) {
var msg = createHostMsg( H2C_SIGNAL_TYPE_ERROR );
msg.errStr = "message_malformed";
socket.send( JSON.stringify( msg ) );
} else if ( msg.signalType == C2H_SIGNAL_TYPE_REGISTER ) {
handleRegistration(socket, msg);
}
}
client:
function initSignalChannel()
{
rtcPeer.channel = new WebSocket( location.origin.replace(/^http/, 'ws') + "/peers" );
rtcPeer.channel.onmessage = updateChannelMessage;
rtcPeer.channel.onopen = function(event) {
console.log("remote socket opened");
}
rtcPeer.channel.onclose = function(event) {
console.log("host closed remote socket.");
}
}
function updateChannelMessage(event) {
var msgObj = JSON.parse(event.data);
if ( !msgObj || !msgObj.signalType ) {
console.log("updateChannelMessage: malformed response!! %o", msgObj );
} else if ( msgObj.signalType == "welcome" ) {
console.log("updateChannelMessage: received welcome from host.");
handleWelcome(msgObj);
} else if ( msgObj.signalType == "peer_joined" ) {
console.log("updateChannelMessage: received peer_joined from host.");
if ( msgObj.peer.id == rtcPeer.description.id ) {
console.log("updateChannelMessage: peer_joined: received notification that I've been added to the room. " + msgObj.peer.id);
console.log(msgObj);
} else {
console.log("updateChannelMessage: peer_joined: peer %s is now online.", msgObj.peer.id);
console.log(msgObj);
addRemotePeer( msgObj.peer );
}
}
}
function addRemotePeer(peerObj)
{
remotePeers[peerObj.id] = peerObj;
var ui = createPeerUIObj(peerObj);
$("#connectedPeerList").append( ui );
}
function createPeerUIObj(peerObj)
{
var ui = null;
if ( peerObj ) {
ui = $("<li></li>");
var a = $("<a></a>");
a.append("peer " + peerObj.id);
ui.append(a);
ui.click(function(event) { console.log("clicked");});
}
return ui;
}
function handleWelcome(msgObj)
{
if ( msgObj.id ) {
console.log("updateChannelMessage: welcome: received id from host. " + msgObj.id);
console.log(msgObj);
rtcPeer.description.id = msgObj.id;
for ( var p in msgObj.peers ) {
addRemotePeer(msgObj.peers[p]);
}
} else {
console.log("updateChannelMessage: malformed response. no id.");
}
}
Thanks for the comments everyone. It turns out that jfriend00 had the right answer, I just didn't realize that the hosting service I was using wouldn't allow for the connection to be kept open.
From the below forum posting, the solution is
you'll need to make your clients ping the server periodically to keep the socket alive.
Not the most ideal situation, but indeed doable. Thanks for pointing me in the right direction.

Disconnect event fired after cilent reconnect - socket.io

I'm using socket.io for realtime communication. When inetrnet is not available for few seconds, then it will connect to server when the connection is establish and then it will immediately trigger event which will log in my clinet. After establishing connection, (i think) my socket.io on server side fire disconnect event after ~one minute (in callback function I log off my client), and that is my problem.
This is my server side. (a have server.js and he run other.js files, in object 'files' a have that files).
server.js :
io = require('socket.io').listen(server);//server listening on 8080 port
io.configure(function () {
io.set('transports', ['websocket', 'xhr-polling']);
io.set('log level', 1);
io.disable('browser client');
});
var files = {
control: null,
terminal: null,
messages: null
};
for (var game in games) {
games[game] = require('./game_modules/' + game + '.js').listen(io, create_waiter(game));
}
require('http').createServer(function (req, res) {
var resultCode = 500;
var resultText = '';
var parts = require('url').parse(req.url, true);
var matches = parts.pathname.match('^/([a-z_-]+)/([a-z_-]+)/$');
if (matches) {
var game = matches[1];
var command = matches[2];
resultText = games[game][command](parts.query);
resultCode = 200;
res.writeHead(resultCode, {'Content-Type': 'text/plain'});
res.end(resultText);
}).listen(8081, "127.0.0.1");
control.js :
var control = module.exports = function() {
};
control.listen = function(io) {
this.server = io.of('/control');
this.onTerminate = onTerminate;
//********************************************************************************
this.server.on('connection', function(socket) {
socket.on('disconnect', function(data) {
console.log(data); //after my client reconnect, disconnect event is triggered, and
//console.log write 'close timeout'
After client reconnect, I do not want trigger disconnect event, or if disconnect event was triggered I want check in callback function: is my client connected and prevent "log off" for my client.
Thanks :)

Categories