I have this utility function which launches a child process. As soon as the child process writes a certain message to stdout, we are supposed to stop listening and fire the callback:
export const launchBrokerInChildProcess = function (opts: any, cb: Function) {
const host = opts.host || 'localhost';
const port = opts.port || 8019;
const detached = Boolean(opts.detached);
ping.probe(host, port, function (err, available) {
if (err) {
return cb(err)
}
if (available) {
log.info(`live-mutex broker/server was already live at ${host}:${port}.`);
return cb(null, {host, port, alreadyRunning: true});
}
log.info(`live-mutex is launching new broker at '${host}:${port}'.`);
const n = cp.spawn('node', [p], {
detached,
env: Object.assign({}, process.env, {
LIVE_MUTEX_PORT: port
})
});
if (detached) {
n.unref();
}
process.once('exit', function () {
if (!detached) {
n.kill('SIGINT');
}
});
n.stderr.setEncoding('utf8');
n.stdout.setEncoding('utf8');
n.stderr.pipe(process.stderr);
let stdout = '';
n.stdout.on('data', function (d) {
stdout += String(d);
if (stdout.match(/live-mutex broker is listening/i)) {
n.stdout.removeAllListeners();
if (detached) {
n.unref();
}
console.log(33333);
cb(null, {
liveMutexProcess: n,
host,
port,
detached
});
}
});
});
};
what's happening is that console.log(33333); is being hit over and over and over again. Very strange - does anyone know why that would happen?
The ping.probe() callback is firing only once, I have confirmed that. So I don't know what's happening.
here is the script that gets launched:
import {Broker} from './broker';
const port = parseInt(process.argv[2] || process.env.LIVE_MUTEX_PORT || '6970');
new Broker({port: port}).ensure().then(function () {
console.log(`live-mutex broker is listening on port ${port}.`);
})
.catch(function (err) {
console.error(err.stack || err);
});
so that line ('live-mutex broker is listening') should only be logged once, given that it's a promise.
Maybe something else uses the port 8019, try to use an other port like 3000.
Related
I use cluster\child_process.fork create multiple child processes, then I found that when I console.log a lot of content in the child process, my child process cannot exit automatically, the same code exits normally when I reduce the output content.
Here is the minimal reproducible code:
const child_process = require('child_process');
const stream = require('stream');
class _Transform extends stream.Transform {
_transform(chunk, encode, next) {
next(null, chunk);
}
}
if (!('IS_WORKER' in process.env)) {
const worker = child_process.fork(process.argv[1], process.argv.slice(2), {
silent: true,
env: {
IS_WORKER: 1,
},
});
const cache = new _Transform();
worker.stdout.pipe(cache);
console.log('master start');
worker.on('message', e => {
console.log(e);
});
worker.on('exit', (code, signal) => {
console.log(code, signal);
cache.pipe(process.stdout);
});
} else {
process.send('hello from child');
console.log(new Array(100000).fill(1).join('')); // It is normal to set a smaller number
process.removeAllListeners();
process.channel.unref();
}
how can I solve this
In fact, I want to collect the console.log content of all subprocesses in the main process and output them at once.
I want to create a CLI application and I think this question is not about a specific technology but for the sake of reproduction purposes I'm using Node with command-line-commands ( but I know there are plenty others, e.g. commander ).
Given the following sample code
#!/usr/bin/env node
'use strict';
const commandLineArgs = require('command-line-args');
const commandLineCommands = require('command-line-commands');
const commandLineUsage = require('command-line-usage');
let isRunning = false; // global state
let commandResult;
try {
commandResult = commandLineCommands([ 'start', 'info', 'help' ]);
} catch (error) {
console.error('Invalid command.');
process.exit(1);
}
if (commandResult.command === null || commandResult.command === 'help') {
const commandInfo = commandLineUsage([
{ header: 'start', content: 'Sets the value to true' },
{ header: 'info', content: 'Gets the current value' },
]);
console.log(commandInfo);
process.exit(0);
}
let options;
try {
options = commandLineArgs([], { argv: commandResult.argv });
} catch (error) {
console.error('Invalid argument.');
process.exit(1);
}
if (commandResult.command === 'start') {
isRunning = true;
} else if (commandResult.command === 'info') {
console.info({ isRunning });
}
The boolean isRunning indicates a shared state. Calling the start command sets its value to true. But calling the info command obviously starts a new process and prints a new variable isRunning with its initial falsy value.
What is the prefered technology to keep such state? Must the CLI use an external database ( e.g. local filesystem) or are there some ways to keep the information in memory until shutdown?
Generating my own file on the system and storing this variable to it feels like an overkill to me.
An old cross-platform hack is to open a known TCP port. The first process able to open the port will get the port. All other processes trying to open the port will get an EADDRINUSE error:
const net = require('net');
const s = net.createServer();
s.on('error',() => {
console.log('Program is already running!');
// handle what to do here
});
s.listen(5123,'127.0.0.1',() => {
console.log('OK');
// run your main function here
});
This works in any language on any OS. There is only one thing you need to be careful of - some other program may be accidentally using the port you are using.
I originally came across this technique on the Tcl wiki: https://wiki.tcl-lang.org/page/singleton+application.
Another old hack for this is to try and create a symlink.
Creating symlinks are generally guaranteed to be atomic by most Unix and Unix-like OSes. Therefore there is no issue with potential race conditions using this technique (unlike creating a regular file). I presume it is also atomic on Windows (as per POSIX spec) but I'm not entirely sure:
const fs = require('fs');
const scriptName = process.argv[1];
const lockFile = '/tmp/my-program.lock';
try {
fs.symlinkSync(lockFile, scriptName);
// run your main function here
fs.unlinkSync(lockFile);
}
catch (err) {
console.log('Program already running');
// handle what to do here
}
Note: While creating symlinks are atomic, other operations on symlinks are not guaranteed to be atomic. Specifically be very careful of assuming that updating a symlink is atomic - it is NOT. Updating symlinks involve two operations: deleting the link and then creating the link. A second process may execute its delete operation after your process creates a symlink causing two processes to think that they're the only ones running. In the example above we delete the link after creating it, not before.
One way would be to use a local web server.
index.js
const commandLineArgs = require('command-line-args');
const commandLineCommands = require('command-line-commands');
const commandLineUsage = require('command-line-usage');
var http = require('http');
let globalState = {
isRunning: false
}
let commandResult;
try {
commandResult = commandLineCommands([ 'start', 'info', 'help' ]);
} catch (error) {
console.error('Invalid command.');
process.exit(1);
}
if (commandResult.command === null || commandResult.command === 'help') {
const commandInfo = commandLineUsage([
{ header: 'start', content: 'Sets the value to true' },
{ header: 'info', content: 'Gets the current value' },
]);
console.log(commandInfo);
process.exit(0);
}
let options;
try {
options = commandLineArgs([], { argv: commandResult.argv });
} catch (error) {
console.error('Invalid argument.');
process.exit(1);
}
if (commandResult.command === 'start') {
globalState.isRunning = true;
http.createServer(function (req, res) {
res.write(JSON.stringify(globalState));
res.end();
}).listen(9615);
} else if (commandResult.command === 'info') {
console.info({ globalState });
}
index2.js
var http = require('http');
var req = http.request({ host: "localhost", port: 9615, path: "/" }, (response) => {
var responseData = "";
response.on("data", (chunk) => {
responseData += chunk;
});
response.on("end", () => {
console.log(JSON.parse(responseData));
});
});
req.end();
req.on("error", (e) => {
console.error(e);
});
Here the index.js is a program that holds the "shared / global state" as well as creates a web server to communicate with. Other programs such as index2.js here can make a http request and ask for the global state. You could also let other programs change the state by having index.js listen to some specific request and act accordingly.
This doesn't have to be done with http like this, you could also use something like node-rpc or node-ipc. I thought the easiest working example would be to do it with a local http client and server.
Either way, I think the word for what you are looking for is Inter Process Communication (IPC) or Remote Procedure Call (RPC). I don't see why one couldn't also utilize websockets as well. Child processes probably won't work here, even if you could implement some kind of parent-child process communication, because only the child processes spawned by the main process could use that.
EDIT
After reading your question more carefully, I think that this is just a matter of "keeping" the "console session" after start command and setting the isRunning variable.
Check this out:
const commandLineArgs = require('command-line-args');
const commandLineCommands = require('command-line-commands');
const commandLineUsage = require('command-line-usage');
const prompt = require('prompt-sync')();
let globalState = {
isRunning: false
}
let commandResult;
try {
commandResult = commandLineCommands([ 'start', 'info', 'help' ]);
} catch (error) {
console.error('Invalid command.');
process.exit(1);
}
if (commandResult.command === null || commandResult.command === 'help') {
const commandInfo = commandLineUsage([
{ header: 'start', content: 'Sets the value to true' },
{ header: 'info', content: 'Gets the current value' },
]);
console.log(commandInfo);
process.exit(0);
}
let options;
try {
options = commandLineArgs([], { argv: commandResult.argv });
} catch (error) {
console.error('Invalid argument.');
process.exit(1);
}
if (commandResult.command === 'start') {
globalState.isRunning = true;
while(globalState.isRunning)
{
let cmd = prompt(">");
if(cmd === "exit")
process.exit(0);
if(cmd === "info")
console.info({ globalState });
}
} else if (commandResult.command === 'info') {
console.info({ globalState });
}
Here I am using prompt-sync library inside a loop when the program is called with a start command. The "console session" is kept indefinitely until the user types exit. I also added and example for in case the user types info.
Example:
Here is db.js file
const client = new MongoClient(DATABASE, mongodbOptions);
const connectWithMongoDb = () => {
client.connect((err) => {
if (err) {
throw err;
} else {
console.log('db connected');
}
});
};
module.exports = { client, connectWithMongoDb };
I called the connectWithMongoDb function from my server.js. db connects successfully. but the problem is I can't reuse the client. for example, I want to make a separate directory for collections. (in order to get a collection I need client object)
So, here is my collection.js file
const { client } = require('../helpers/db-helper/db');
exports.collection = client.db('organicdb').collection('products');
but the problem arises as soon as this file(collection.js) is called.
I am getting this error:
throw new MongoError('MongoClient must be connected before calling MongoClient.prototype.db'
You have to get the connection after connecting to MongoDB post that you can use it anywhere.
Read - https://mongodb.github.io/node-mongodb-native/api-generated/mongoclient.html
let client;
async function connect() {
if (!client) {
client = await MongoClient.connect(DATABASE, mongodbOptions)
.catch(err => { console.log(err); });
}
return client;
}
conet getConectedClient = () => client;
const testConnection = connect()
.then((connection) => console.log(connection)); // call the function like this
module.exports = { connect, getConectedClient };
I have a Windows Service polling periodically whether my app in running or not. When it is, the service checks for an update on a server. If there is an update, I need a way for the service to let the app know it is available so the user can be notified.
Is this possible?
You can use one of the IPC means, by creating a named pipe (socket)
//"PIPE_NAME" :"the_name_of_the_pipe",
//"PIPE_PATH" : "\\\\.\\pipe\\"
const net = require('net');
let socketClient = net.createConnection(config.get('PIPE_PATH') + config.get('PIPE_NAME'), () => {
console.log('connected to service!');
});
socketClient.on('data', (data) => {
console.log(data.toString());
});
socketClient.on('error', (error) => {
//console.log(`Couldn't connect to service : ${error.toString()}`);
setTimeout(() => {
socketClient.connect(config.get('PIPE_PATH') + config.get('PIPE_NAME'), () => {
console.log('connected to service!');
});
}, 4000);
});
socketClient.on('end', () => {
console.log('disconnected from service');
socketClient.connect(config.get('PIPE_PATH') + config.get('PIPE_NAME'), () => {
console.log('connected to service!');
});
});
I used Qt for the service side and created QLocalServer to exchange messages between the service and electron app but you can use and find many examples out there
you can create a namedpipe that listens
//https://stackoverflow.com/a/26561999/3339316
HANDLE hPipe;
char buffer[1024];
DWORD dwRead;
hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\Pipe"),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, // FILE_FLAG_FIRST_PIPE_INSTANCE is not needed but forces CreateNamedPipe(..) to fail if the pipe already exists...
1,
1024 * 16,
1024 * 16,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
while (hPipe != INVALID_HANDLE_VALUE)
{
if (ConnectNamedPipe(hPipe, NULL) != FALSE) // wait for someone to connect to the pipe
{
while (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL) != FALSE)
{
/* add terminating zero */
buffer[dwRead] = '\0';
/* do something with data in buffer */
printf("%s", buffer);
}
}
DisconnectNamedPipe(hPipe);
}
How can I check if port is busy for localhost?
Is there any standard algorithm? I am thinking at making a http request to that url and check if response status code is not 404.
You could attempt to start a server, either TCP or HTTP, it doesn't matter. Then you could try to start listening on a port, and if it fails, check if the error code is EADDRINUSE.
var net = require('net');
var server = net.createServer();
server.once('error', function(err) {
if (err.code === 'EADDRINUSE') {
// port is currently in use
}
});
server.once('listening', function() {
// close the server if listening doesn't fail
server.close();
});
server.listen(/* put the port to check here */);
With the single-use event handlers, you could wrap this into an asynchronous check function.
Check out the amazing tcp-port-used node module!
//Check if a port is open
tcpPortUsed.check(port [, host])
//Wait until a port is no longer being used
tcpPortUsed.waitUntilFree(port [, retryTimeMs] [, timeOutMs])
//Wait until a port is accepting connections
tcpPortUsed.waitUntilUsed(port [, retryTimeMs] [, timeOutMs])
//and a few others!
I've used these to great effect with my gulp watch tasks for detecting when my Express server has been safely terminated and when it has spun up again.
This will accurately report whether a port is bound or not (regardless of SO_REUSEADDR and SO_REUSEPORT, as mentioned by #StevenVachon).
The portscanner NPM module will find free and used ports for you within ranges and is more useful if you're trying to find an open port to bind.
Thank to Steven Vachon link, I made a simple example:
const net = require("net");
const Socket = net.Socket;
const getNextPort = async (port) =>
{
return new Promise((resolve, reject) =>
{
const socket = new Socket();
const timeout = () =>
{
resolve(port);
socket.destroy();
};
const next = () =>
{
socket.destroy();
resolve(getNextPort(++port));
};
setTimeout(timeout, 10);
socket.on("timeout", timeout);
socket.on("connect", () => next());
socket.on("error", error =>
{
if (error.code !== "ECONNREFUSED")
reject(error);
else
resolve(port);
});
socket.connect(port, "0.0.0.0");
});
};
getNextPort(8080).then(port => {
console.log("port", port);
});
this is what im doing, i hope it help someone
const isPortOpen = async (port: number): Promise<boolean> => {
return new Promise((resolve, reject) => {
let s = net.createServer();
s.once('error', (err) => {
s.close();
if (err["code"] == "EADDRINUSE") {
resolve(false);
} else {
resolve(false); // or throw error!!
// reject(err);
}
});
s.once('listening', () => {
resolve(true);
s.close();
});
s.listen(port);
});
}
const getNextOpenPort = async(startFrom: number = 2222) => {
let openPort: number = null;
while (startFrom < 65535 || !!openPort) {
if (await isPortOpen(startFrom)) {
openPort = startFrom;
break;
}
startFrom++;
}
return openPort;
};
you can use isPortOpen if you just need to check if a port is busy or not.
and the getNextOpenPort finds next open port after startFrom. for example :
let startSearchingFrom = 1024;
let port = await getNextOpenPort(startSearchingFrom);
console.log(port);